Ver Fonte

Fix compatibility with twig-bridge 3.2 form renderer (#4216)

Our code was relying on an undeclared private property of the form Twig
extension to get a renderer object, in order to set the form theme. On recent
versions of Symfony, the renderer is now obtained from the Twig environment.
Konstantin Myakshin há 8 anos atrás
pai
commit
d8fc2edae1

+ 30 - 9
Controller/CRUDController.php

@@ -23,6 +23,7 @@ use Sonata\AdminBundle\Util\AdminObjectAclManipulator;
 use Symfony\Bundle\FrameworkBundle\Controller\Controller;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\Form\Form;
+use Symfony\Component\Form\FormView;
 use Symfony\Component\HttpFoundation\JsonResponse;
 use Symfony\Component\HttpFoundation\RedirectResponse;
 use Symfony\Component\HttpFoundation\Request;
@@ -105,7 +106,7 @@ class CRUDController extends Controller
         $formView = $datagrid->getForm()->createView();
 
         // set the theme for the current Admin Form
-        $this->get('twig')->getExtension('Symfony\Bridge\Twig\Extension\FormExtension')->renderer->setTheme($formView, $this->admin->getFilterTheme());
+        $this->setFormTheme($formView);
 
         return $this->render($this->admin->getTemplate('list'), array(
             'action' => 'list',
@@ -318,14 +319,13 @@ class CRUDController extends Controller
             }
         }
 
-        $view = $form->createView();
-
+        $formView = $form->createView();
         // set the theme for the current Admin Form
-        $this->get('twig')->getExtension('Symfony\Bridge\Twig\Extension\FormExtension')->renderer->setTheme($view, $this->admin->getFormTheme());
+        $this->setFormTheme($formView);
 
         return $this->render($this->admin->getTemplate($templateKey), array(
             'action' => 'edit',
-            'form' => $view,
+            'form' => $formView,
             'object' => $object,
         ), null);
     }
@@ -557,14 +557,13 @@ class CRUDController extends Controller
             }
         }
 
-        $view = $form->createView();
-
+        $formView = $form->createView();
         // set the theme for the current Admin Form
-        $this->get('twig')->getExtension('Symfony\Bridge\Twig\Extension\FormExtension')->renderer->setTheme($view, $this->admin->getFormTheme());
+        $this->setFormTheme($formView);
 
         return $this->render($this->admin->getTemplate($templateKey), array(
             'action' => 'create',
-            'form' => $view,
+            'form' => $formView,
             'object' => $object,
         ), null);
     }
@@ -1364,4 +1363,26 @@ class CRUDController extends Controller
 
         return $this->get('translator')->trans($id, $parameters, $domain, $locale);
     }
+
+    /**
+     * Sets the admin form theme to form view. Used for compatibility between Symfony versions.
+     *
+     * @param FormView $formView
+     */
+    private function setFormTheme(FormView $formView)
+    {
+        $twig = $this->get('twig');
+
+        try {
+            $twig
+                ->getRuntime('Symfony\Bridge\Twig\Form\TwigRenderer')
+                ->setTheme($formView, $this->admin->getFilterTheme());
+        } catch (\Twig_Error_Runtime $e) {
+            // BC for Symfony < 3.2 where this runtime not exists
+            $twig
+                ->getExtension('Symfony\Bridge\Twig\Extension\FormExtension')
+                ->renderer
+                ->setTheme($formView, $this->admin->getFilterTheme());
+        }
+    }
 }

+ 26 - 11
Controller/HelperController.php

@@ -15,6 +15,7 @@ use Sonata\AdminBundle\Admin\AdminHelper;
 use Sonata\AdminBundle\Admin\AdminInterface;
 use Sonata\AdminBundle\Admin\Pool;
 use Sonata\AdminBundle\Filter\FilterInterface;
+use Symfony\Bridge\Twig\Form\TwigRenderer;
 use Symfony\Component\Form\FormInterface;
 use Symfony\Component\HttpFoundation\JsonResponse;
 use Symfony\Component\HttpFoundation\Request;
@@ -108,13 +109,10 @@ class HelperController
         $view = $this->helper->getChildFormView($form->createView(), $elementId);
 
         // render the widget
-        // todo : fix this, the twig environment variable is not set inside the extension ...
-
-        $extension = $this->twig->getExtension('Symfony\Bridge\Twig\Extension\FormExtension');
-        $extension->initRuntime($this->twig);
-        $extension->renderer->setTheme($view, $admin->getFormTheme());
+        $renderer = $this->getFormRenderer();
+        $renderer->setTheme($view, $admin->getFormTheme());
 
-        return new Response($extension->renderer->searchAndRenderBlock($view, 'widget'));
+        return new Response($renderer->searchAndRenderBlock($view, 'widget'));
     }
 
     /**
@@ -157,12 +155,10 @@ class HelperController
         $view = $this->helper->getChildFormView($form->createView(), $elementId);
 
         // render the widget
-        // todo : fix this, the twig environment variable is not set inside the extension ...
-        $extension = $this->twig->getExtension('Symfony\Bridge\Twig\Extension\FormExtension');
-        $extension->initRuntime($this->twig);
-        $extension->renderer->setTheme($view, $admin->getFormTheme());
+        $renderer = $this->getFormRenderer();
+        $renderer->setTheme($view, $admin->getFormTheme());
 
-        return new Response($extension->renderer->searchAndRenderBlock($view, 'widget'));
+        return new Response($renderer->searchAndRenderBlock($view, 'widget'));
     }
 
     /**
@@ -497,4 +493,23 @@ class HelperController
 
         return $fieldDescription;
     }
+
+    /**
+     * @return TwigRenderer
+     */
+    private function getFormRenderer()
+    {
+        try {
+            $runtime = $this->twig->getRuntime('Symfony\Bridge\Twig\Form\TwigRenderer');
+            $runtime->setEnvironment($this->twig);
+
+            return $runtime;
+        } catch (\Twig_Error_Runtime $e) {
+            // BC for Symfony < 3.2 where this runtime not exists
+            $extension = $this->twig->getExtension('Symfony\Bridge\Twig\Extension\FormExtension');
+            $extension->initRuntime($this->twig);
+
+            return $extension->renderer;
+        }
+    }
 }

+ 13 - 0
Tests/Controller/CRUDControllerTest.php

@@ -187,6 +187,19 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
                 }
             }));
 
+        $twig->expects($this->any())
+            ->method('getRuntime')
+            ->will($this->returnCallback(function ($name) use ($twigRenderer) {
+                switch ($name) {
+                    case 'Symfony\Bridge\Twig\Form\TwigRenderer':
+                        if (method_exists('Symfony\Bridge\Twig\AppVariable', 'getToken')) {
+                            return $twigRenderer;
+                        }
+
+                        throw new \Twig_Error_Runtime('This runtime exists when Symony >= 3.2.');
+                }
+            }));
+
         // NEXT_MAJOR : require sonata/exporter ^1.7 and remove conditional
         if (class_exists('Exporter\Exporter')) {
             $exporter = new Exporter(array(new JsonWriter('/tmp/sonataadmin/export.json')));

+ 32 - 6
Tests/Controller/HelperControllerTest.php

@@ -105,7 +105,7 @@ class HelperControllerTest extends \PHPUnit_Framework_TestCase
     }
 
     /**
-     * @expectedException Symfony\Component\HttpKernel\Exception\NotFoundHttpException
+     * @expectedException \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
      * @dataProvider getValidatorInterfaces
      */
     public function testgetShortObjectDescriptionActionInvalidAdmin($validatorInterface)
@@ -328,6 +328,20 @@ class HelperControllerTest extends \PHPUnit_Framework_TestCase
 
         $twig = new \Twig_Environment($this->getMock('\Twig_LoaderInterface'));
         $twig->addExtension(new FormExtension($mockRenderer));
+
+        if (method_exists('Symfony\Bridge\Twig\AppVariable', 'getToken')) {
+            $runtimeLoader = $this
+                ->getMockBuilder('Twig_RuntimeLoaderInterface')
+                ->getMock();
+
+            $runtimeLoader->expects($this->once())
+                ->method('load')
+                ->with($this->equalTo('Symfony\Bridge\Twig\Form\TwigRenderer'))
+                ->will($this->returnValue($mockRenderer));
+
+            $twig->addRuntimeLoader($runtimeLoader);
+        }
+
         $request = new Request(array(
             'code' => 'sonata.post.admin',
             'objectId' => 42,
@@ -368,7 +382,7 @@ class HelperControllerTest extends \PHPUnit_Framework_TestCase
     /**
      * @dataProvider getValidatorInterfaces
      */
-    public function testretrieveFormFieldElementAction($validatorInterface)
+    public function testRetrieveFormFieldElementAction($validatorInterface)
     {
         $object = new AdminControllerHelper_Foo();
 
@@ -408,6 +422,18 @@ class HelperControllerTest extends \PHPUnit_Framework_TestCase
 
         $twig = new \Twig_Environment($this->getMock('\Twig_LoaderInterface'));
         $twig->addExtension(new FormExtension($mockRenderer));
+        if (method_exists('Symfony\Bridge\Twig\AppVariable', 'getToken')) {
+            $runtimeLoader = $this
+                ->getMockBuilder('Twig_RuntimeLoaderInterface')
+                ->getMock();
+
+            $runtimeLoader->expects($this->once())
+                ->method('load')
+                ->with($this->equalTo('Symfony\Bridge\Twig\Form\TwigRenderer'))
+                ->will($this->returnValue($mockRenderer));
+
+            $twig->addRuntimeLoader($runtimeLoader);
+        }
         $request = new Request(array(
             'code' => 'sonata.post.admin',
             'objectId' => 42,
@@ -487,7 +513,7 @@ class HelperControllerTest extends \PHPUnit_Framework_TestCase
     }
 
     /**
-     * @expectedException Symfony\Component\Security\Core\Exception\AccessDeniedException
+     * @expectedException \Symfony\Component\Security\Core\Exception\AccessDeniedException
      * @exceptionMessage Invalid format
      */
     public function testRetrieveAutocompleteItemsActionNotGranted()
@@ -510,7 +536,7 @@ class HelperControllerTest extends \PHPUnit_Framework_TestCase
     }
 
     /**
-     * @expectedException Symfony\Component\Security\Core\Exception\AccessDeniedException
+     * @expectedException \Symfony\Component\Security\Core\Exception\AccessDeniedException
      * @exceptionMessage Invalid format
      */
     public function testRetrieveFilterAutocompleteItemsActionNotGranted()
@@ -534,7 +560,7 @@ class HelperControllerTest extends \PHPUnit_Framework_TestCase
     }
 
     /**
-     * @expectedException Symfony\Component\Security\Core\Exception\AccessDeniedException
+     * @expectedException \Symfony\Component\Security\Core\Exception\AccessDeniedException
      * @exceptionMessage Autocomplete list can`t be retrieved because the form element is disabled or read_only.
      */
     public function testRetrieveAutocompleteItemsActionDisabledFormelememt()
@@ -604,7 +630,7 @@ class HelperControllerTest extends \PHPUnit_Framework_TestCase
     }
 
     /**
-     * @expectedException Symfony\Component\Security\Core\Exception\AccessDeniedException
+     * @expectedException \Symfony\Component\Security\Core\Exception\AccessDeniedException
      */
     public function testRetrieveAutocompleteItemsActionNotGrantedTarget()
     {

+ 1 - 2
Tests/Twig/Extension/SonataAdminExtensionTest.php

@@ -1297,7 +1297,7 @@ EOT
     }
 
     /**
-     * @expectedException        Twig_Error_Loader
+     * @expectedException        \Twig_Error_Loader
      * @expectedExceptionMessage Unable to find template "base_list_nonexistent_field.html.twig"
      * @group                    legacy
      */
@@ -1375,7 +1375,6 @@ EOT
                 }
             }));
 
-
         $this->assertSame(
                 $this->removeExtraWhitespace($expected),
                 $this->removeExtraWhitespace(