Browse Source

Added preCreate, preEdit, preDelete, preShow, preList methods in the CRUDController

Andrej Hudec 10 years ago
parent
commit
03392c2e07

+ 99 - 16
Controller/CRUDController.php

@@ -246,6 +246,11 @@ class CRUDController extends Controller
             throw new AccessDeniedException();
         }
 
+        $preResponse = $this->preList($request);
+        if ($preResponse !== null) {
+            return $preResponse;
+        }
+
         if ($listMode = $request->get('_list_mode')) {
             $this->admin->setListMode($listMode);
         }
@@ -284,7 +289,6 @@ class CRUDController extends Controller
             $modelManager->batchDelete($this->admin->getClass(), $query);
             $this->addFlash('sonata_flash_success', 'flash_batch_delete_success');
         } catch (ModelManagerException $e) {
-
             $this->handleModelManagerException($e);
             $this->addFlash('sonata_flash_error', 'flash_batch_delete_error');
         }
@@ -320,12 +324,17 @@ class CRUDController extends Controller
             throw new AccessDeniedException();
         }
 
-        if ($this->getRestMethod($request) == 'DELETE') {
+        $preResponse = $this->preDelete($request, $object);
+        if ($preResponse !== null) {
+            return $preResponse;
+        }
+
+        if ($this->getRestMethod($request) === 'DELETE') {
             // check the csrf token
             $this->validateCsrfToken('sonata.delete', $request);
 
             $objectName = $this->admin->toString($object);
-            
+
             try {
                 $this->admin->delete($object);
 
@@ -341,7 +350,6 @@ class CRUDController extends Controller
                         'SonataAdminBundle'
                     )
                 );
-
             } catch (ModelManagerException $e) {
                 $this->handleModelManagerException($e);
 
@@ -365,7 +373,7 @@ class CRUDController extends Controller
         return $this->render($this->admin->getTemplate('delete'), array(
             'object'     => $object,
             'action'     => 'delete',
-            'csrf_token' => $this->getCsrfToken('sonata.delete')
+            'csrf_token' => $this->getCsrfToken('sonata.delete'),
         ), null, $request);
     }
 
@@ -397,27 +405,31 @@ class CRUDController extends Controller
             throw new AccessDeniedException();
         }
 
+        $preResponse = $this->preEdit($request, $object);
+        if ($preResponse !== null) {
+            return $preResponse;
+        }
+
         $this->admin->setSubject($object);
 
         /** @var $form \Symfony\Component\Form\Form */
         $form = $this->admin->getForm();
         $form->setData($object);
 
-        if ($this->getRestMethod($request) == 'POST') {
+        if ($this->getRestMethod($request) === 'POST') {
             $form->submit($request);
 
             $isFormValid = $form->isValid();
 
             // persist if the form was valid and if in preview mode the preview was approved
             if ($isFormValid && (!$this->isInPreviewMode($request) || $this->isPreviewApproved($request))) {
-
                 try {
                     $object = $this->admin->update($object);
 
                     if ($this->isXmlHttpRequest($request)) {
                         return $this->renderJson(array(
                             'result'    => 'ok',
-                            'objectId'  => $this->admin->getNormalizedIdentifier($object)
+                            'objectId'  => $this->admin->getNormalizedIdentifier($object),
                         ), 200, array(), $request);
                     }
 
@@ -432,7 +444,6 @@ class CRUDController extends Controller
 
                     // redirect to edit mode
                     return $this->redirectTo($object, $request);
-
                 } catch (ModelManagerException $e) {
                     $this->handleModelManagerException($e);
 
@@ -500,7 +511,7 @@ class CRUDController extends Controller
             $url = $this->admin->generateUrl('create', $params);
         }
 
-        if ($this->getRestMethod($request) == 'DELETE') {
+        if ($this->getRestMethod($request) === 'DELETE') {
             $url = $this->admin->generateUrl('list');
         }
 
@@ -646,20 +657,24 @@ class CRUDController extends Controller
 
         $object = $this->admin->getNewInstance();
 
+        $preResponse = $this->preCreate($request, $object);
+        if ($preResponse !== null) {
+            return $preResponse;
+        }
+
         $this->admin->setSubject($object);
 
         /** @var $form \Symfony\Component\Form\Form */
         $form = $this->admin->getForm();
         $form->setData($object);
 
-        if ($this->getRestMethod($request) == 'POST') {
+        if ($this->getRestMethod($request) === 'POST') {
             $form->submit($request);
 
             $isFormValid = $form->isValid();
 
             // persist if the form was valid and if in preview mode the preview was approved
             if ($isFormValid && (!$this->isInPreviewMode($request) || $this->isPreviewApproved($request))) {
-
                 if (false === $this->admin->isGranted('CREATE', $object)) {
                     throw new AccessDeniedException();
                 }
@@ -670,7 +685,7 @@ class CRUDController extends Controller
                     if ($this->isXmlHttpRequest($request)) {
                         return $this->renderJson(array(
                             'result' => 'ok',
-                            'objectId' => $this->admin->getNormalizedIdentifier($object)
+                            'objectId' => $this->admin->getNormalizedIdentifier($object),
                         ), 200, array(), $request);
                     }
 
@@ -685,7 +700,6 @@ class CRUDController extends Controller
 
                     // redirect to edit mode
                     return $this->redirectTo($object, $request);
-
                 } catch (ModelManagerException $e) {
                     $this->handleModelManagerException($e);
 
@@ -812,6 +826,11 @@ class CRUDController extends Controller
             throw new AccessDeniedException();
         }
 
+        $preResponse = $this->preShow($request, $object);
+        if ($preResponse !== null) {
+            return $preResponse;
+        }
+
         $this->admin->setSubject($object);
 
         return $this->render($this->admin->getTemplate('show'), array(
@@ -1007,7 +1026,7 @@ class CRUDController extends Controller
             'action'            => 'show',
             'object'            => $base_object,
             'object_compare'    => $compare_object,
-            'elements'          => $this->admin->getShow()
+            'elements'          => $this->admin->getShow(),
         ), null, $request);
     }
 
@@ -1189,7 +1208,7 @@ class CRUDController extends Controller
             'users'        => $aclUsers,
             'roles'        => $aclRoles,
             'aclUsersForm' => $aclUsersForm->createView(),
-            'aclRolesForm' => $aclRolesForm->createView()
+            'aclRolesForm' => $aclRolesForm->createView(),
         ), null, $request);
     }
 
@@ -1258,6 +1277,70 @@ class CRUDController extends Controller
         return $this->container->get('form.csrf_provider')->generateCsrfToken($intention);
     }
 
+    /**
+     * This method can be overloaded in your custom CRUD controller.
+     * It's called from createAction.
+     *
+     * @param Request $request
+     * @param mixed   $object
+     *
+     * @return Response|null
+     */
+    protected function preCreate(Request $request, $object)
+    {
+    }
+
+    /**
+     * This method can be overloaded in your custom CRUD controller.
+     * It's called from editAction.
+     *
+     * @param Request $request
+     * @param mixed   $object
+     *
+     * @return Response|null
+     */
+    protected function preEdit(Request $request, $object)
+    {
+    }
+
+    /**
+     * This method can be overloaded in your custom CRUD controller.
+     * It's called from deleteAction.
+     *
+     * @param Request $request
+     * @param mixed   $object
+     *
+     * @return Response|null
+     */
+    protected function preDelete(Request $request, $object)
+    {
+    }
+
+    /**
+     * This method can be overloaded in your custom CRUD controller.
+     * It's called from showAction.
+     *
+     * @param Request $request
+     * @param mixed   $object
+     *
+     * @return Response|null
+     */
+    protected function preShow(Request $request, $object)
+    {
+    }
+
+    /**
+     * This method can be overloaded in your custom CRUD controller.
+     * It's called from listAction.
+     *
+     * @param Request $request
+     *
+     * @return Response|null
+     */
+    protected function preList(Request $request)
+    {
+    }
+
     /**
      * To keep backwards compatibility with older Sonata Admin code.
      *

+ 12 - 0
Resources/doc/reference/architecture.rst

@@ -147,6 +147,18 @@ a set of automatically injected dependencies that are useful when implementing s
 scenarios. Refer to the existing ``CRUDController`` actions for examples of how to get
 the best out of them.
 
+In your overloaded CRUDController you can overload also these methods to limit
+the number of duplicated code from SonataAdmin:
+* ``preCreate``: called from ``createAction``
+* ``preEdit``: called from ``editAction``
+* ``preDelete``: called from ``deleteAction``
+* ``preShow``: called from ``showAction``
+* ``preList``: called from ``listAction``
+
+These methods are called after checking the access rights and after retrieving the object
+from database. You can use them if you need to redirect user to some other page under certain conditions.
+
+
 Fields Definition
 -----------------
 

+ 262 - 42
Tests/Controller/CRUDControllerTest.php

@@ -30,6 +30,7 @@ use Symfony\Component\HttpKernel\KernelInterface;
 use Symfony\Component\HttpKernel\Exception\HttpException;
 use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface;
 use Sonata\AdminBundle\Admin\AdminInterface;
+use Sonata\AdminBundle\Tests\Fixtures\Controller\PreCRUDController;
 
 /**
  * Test for CRUDController
@@ -173,7 +174,7 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
                         return $formExtension;
                 }
 
-                return null;
+                return;
             }));
 
         $exporter = $this->getMock('Sonata\AdminBundle\Export\Exporter');
@@ -277,7 +278,7 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
                         return $kernel;
                 }
 
-                return null;
+                return;
             }));
 
         // php 5.3
@@ -286,7 +287,7 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
         $this->container->expects($this->any())
             ->method('has')
             ->will($this->returnCallback(function ($id) use ($tthis) {
-                if ($id == 'form.csrf_provider' && $tthis->getCsrfProvider()!==null) {
+                if ($id == 'form.csrf_provider' && $tthis->getCsrfProvider() !== null) {
                     return true;
                 }
 
@@ -305,7 +306,7 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
                        return array('ROLE_SUPER_ADMIN' => array('ROLE_USER', 'ROLE_SONATA_ADMIN', 'ROLE_ADMIN'));
                 }
 
-                return null;
+                return;
             }));
 
         $this->admin->expects($this->any())
@@ -342,7 +343,7 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
                         return 'SonataAdminBundle:CRUD:batch_confirmation.html.twig';
                 }
 
-                return null;
+                return;
             }));
 
         $this->admin->expects($this->any())
@@ -389,7 +390,7 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
             'configure',
             'getBaseTemplate',
             'redirectTo',
-            'addFlash'
+            'addFlash',
         );
         foreach ($testedMethods as $testedMethod) {
             $method = new \ReflectionMethod('Sonata\\AdminBundle\\Controller\\CRUDController', $testedMethod);
@@ -400,7 +401,7 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
 
     public function testRenderJson1()
     {
-        $data = array('example'=>'123', 'foo'=>'bar');
+        $data = array('example' => '123', 'foo' => 'bar');
 
         $this->request->headers->set('Content-Type', 'application/x-www-form-urlencoded');
         $response = $this->protectedTestedMethods['renderJson']->invoke($this->controller, $data, 200, array(), $this->request);
@@ -411,7 +412,7 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
 
     public function testRenderJson2()
     {
-        $data = array('example'=>'123', 'foo'=>'bar');
+        $data = array('example' => '123', 'foo' => 'bar');
 
         $this->request->headers->set('Content-Type', 'multipart/form-data');
         $response = $this->protectedTestedMethods['renderJson']->invoke($this->controller, $data, 200, array(), $this->request);
@@ -422,7 +423,7 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
 
     public function testRenderJsonAjax()
     {
-        $data = array('example'=>'123', 'foo'=>'bar');
+        $data = array('example' => '123', 'foo' => 'bar');
 
         $this->request->attributes->set('_xml_http_request', true);
         $this->request->headers->set('Content-Type', 'multipart/form-data');
@@ -505,7 +506,7 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
     {
         $this->setExpectedException(
             'RuntimeException',
-            'Unable to find the admin class related to the current controller ' .
+            'Unable to find the admin class related to the current controller '.
             '(Sonata\AdminBundle\Controller\CRUDController)'
         );
 
@@ -574,7 +575,7 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
         $this->assertInstanceOf(
             'Symfony\Component\HttpFoundation\Response',
             $this->controller->render('FooAdminBundle::foo.html.twig',
-            array('foo'=>'bar'), null, $this->request)
+            array('foo' => 'bar'), null, $this->request)
         );
         $this->assertEquals($this->admin, $this->parameters['admin']);
         $this->assertEquals('SonataAdminBundle::standard_layout.html.twig', $this->parameters['base_template']);
@@ -587,7 +588,7 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
     {
         $this->parameters = array();
         $this->request->headers->set('X-Requested-With', 'XMLHttpRequest');
-        $this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $this->controller->render('FooAdminBundle::foo.html.twig', array('foo'=>'bar'), null, $this->request));
+        $this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $this->controller->render('FooAdminBundle::foo.html.twig', array('foo' => 'bar'), null, $this->request));
         $this->assertEquals($this->admin, $this->parameters['admin']);
         $this->assertEquals('SonataAdminBundle::ajax_layout.html.twig', $this->parameters['base_template']);
         $this->assertEquals($this->pool, $this->parameters['admin_pool']);
@@ -607,6 +608,21 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
         $this->controller->listAction($this->request);
     }
 
+    public function testPreList()
+    {
+        $this->admin->expects($this->once())
+            ->method('isGranted')
+            ->with($this->equalTo('LIST'))
+            ->will($this->returnValue(true));
+
+        $controller = new PreCRUDController();
+        $controller->setContainer($this->container);
+
+        $response = $controller->listAction($this->request);
+        $this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response);
+        $this->assertEquals('preList called', $response->getContent());
+    }
+
     public function testListAction()
     {
         $datagrid = $this->getMock('Sonata\AdminBundle\Datagrid\DatagridInterface');
@@ -620,7 +636,7 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
             ->disableOriginalConstructor()
             ->getMock();
 
-         $form->expects($this->once())
+        $form->expects($this->once())
             ->method('createView')
             ->will($this->returnValue($this->getMock('Symfony\Component\Form\FormView')));
 
@@ -674,7 +690,7 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
 
         $this->admin->expects($this->once())
             ->method('getFilterParameters')
-            ->will($this->returnValue(array('foo'=>'bar')));
+            ->will($this->returnValue(array('foo' => 'bar')));
 
         $result = $this->controller->batchActionDelete($this->getMock('Sonata\AdminBundle\Datagrid\ProxyQueryInterface'));
 
@@ -701,7 +717,7 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
             ->method('error')
             ->with($message, array(
                 'exception' => $exception,
-                'previous_exception_message' => $previousExceptionMessage
+                'previous_exception_message' => $previousExceptionMessage,
             ));
     }
 
@@ -716,7 +732,7 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
 
         $this->admin->expects($this->once())
             ->method('getFilterParameters')
-            ->will($this->returnValue(array('foo'=>'bar')));
+            ->will($this->returnValue(array('foo' => 'bar')));
 
         $result = $this->controller->batchActionDelete($this->getMock('Sonata\AdminBundle\Datagrid\ProxyQueryInterface'));
 
@@ -774,6 +790,28 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
         $this->controller->showAction(null, $this->request);
     }
 
+    public function testPreShow()
+    {
+        $object = new \stdClass();
+        $object->foo = 123456;
+
+        $this->admin->expects($this->once())
+            ->method('getObject')
+            ->will($this->returnValue($object));
+
+        $this->admin->expects($this->once())
+            ->method('isGranted')
+            ->with($this->equalTo('VIEW'))
+            ->will($this->returnValue(true));
+
+        $controller = new PreCRUDController();
+        $controller->setContainer($this->container);
+
+        $response = $controller->showAction(null, $this->request);
+        $this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response);
+        $this->assertEquals('preShow called: 123456', $response->getContent());
+    }
+
     public function testShowAction()
     {
         $object = new \stdClass();
@@ -831,10 +869,10 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
     {
         return array(
             array('stdClass_edit', array(), false),
-            array('list', array('btn_update_and_list'=>true), false),
-            array('list', array('btn_create_and_list'=>true), false),
-            array('create', array('btn_create_and_create'=>true), false),
-            array('create?subclass=foo', array('btn_create_and_create'=>true, 'subclass'=>'foo'), true),
+            array('list', array('btn_update_and_list' => true), false),
+            array('list', array('btn_create_and_list' => true), false),
+            array('create', array('btn_create_and_create' => true), false),
+            array('create?subclass=foo', array('btn_create_and_create' => true, 'subclass' => 'foo'), true),
         );
     }
 
@@ -871,6 +909,28 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
         $this->controller->deleteAction(1, $this->request);
     }
 
+    public function testPreDelete()
+    {
+        $object = new \stdClass();
+        $object->foo = 123456;
+
+        $this->admin->expects($this->once())
+            ->method('getObject')
+            ->will($this->returnValue($object));
+
+        $this->admin->expects($this->once())
+            ->method('isGranted')
+            ->with($this->equalTo('DELETE'))
+            ->will($this->returnValue(true));
+
+        $controller = new PreCRUDController();
+        $controller->setContainer($this->container);
+
+        $response = $controller->deleteAction(null, $this->request);
+        $this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response);
+        $this->assertEquals('preDelete called: 123456', $response->getContent());
+    }
+
     public function testDeleteAction()
     {
         $object = new \stdClass();
@@ -948,7 +1008,7 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
         $response = $this->controller->deleteAction(1, $this->request);
 
         $this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response);
-        $this->assertEquals(json_encode(array('result'=>'ok')), $response->getContent());
+        $this->assertEquals(json_encode(array('result' => 'ok')), $response->getContent());
         $this->assertEquals(array(), $this->session->getFlashBag()->all());
     }
 
@@ -974,7 +1034,7 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
         $response = $this->controller->deleteAction(1, $this->request);
 
         $this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response);
-        $this->assertEquals(json_encode(array('result'=>'ok')), $response->getContent());
+        $this->assertEquals(json_encode(array('result' => 'ok')), $response->getContent());
         $this->assertEquals(array(), $this->session->getFlashBag()->all());
     }
 
@@ -1001,7 +1061,7 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
         $response = $this->controller->deleteAction(1, $this->request);
 
         $this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response);
-        $this->assertEquals(json_encode(array('result'=>'error')), $response->getContent());
+        $this->assertEquals(json_encode(array('result' => 'error')), $response->getContent());
         $this->assertEquals(array(), $this->session->getFlashBag()->all());
     }
 
@@ -1034,7 +1094,6 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
         $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.delete');
 
         $this->controller->deleteAction(1, $this->request);
-
     }
 
     /**
@@ -1258,6 +1317,28 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
         $this->controller->editAction(null, $this->request);
     }
 
+    public function testPreEdit()
+    {
+        $object = new \stdClass();
+        $object->foo = 123456;
+
+        $this->admin->expects($this->once())
+            ->method('getObject')
+            ->will($this->returnValue($object));
+
+        $this->admin->expects($this->once())
+            ->method('isGranted')
+            ->with($this->equalTo('EDIT'))
+            ->will($this->returnValue(true));
+
+        $controller = new PreCRUDController();
+        $controller->setContainer($this->container);
+
+        $response = $controller->editAction(null, $this->request);
+        $this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response);
+        $this->assertEquals('preEdit called: 123456', $response->getContent());
+    }
+
     public function testEditAction()
     {
         $object = new \stdClass();
@@ -1399,7 +1480,7 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
         $this->assertInstanceOf('Symfony\Component\Form\FormView', $this->parameters['form']);
         $this->assertEquals($object, $this->parameters['object']);
 
-        $this->assertEquals(array('sonata_flash_error'=>array('flash_edit_error')), $this->session->getFlashBag()->all());
+        $this->assertEquals(array('sonata_flash_error' => array('flash_edit_error')), $this->session->getFlashBag()->all());
         $this->assertEquals('SonataAdminBundle:CRUD:edit.html.twig', $this->template);
     }
 
@@ -1443,7 +1524,7 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
         $response = $this->controller->editAction(null, $this->request);
 
         $this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response);
-        $this->assertEquals(json_encode(array('result'=>'ok', 'objectId'  => 'foo_normalized')), $response->getContent());
+        $this->assertEquals(json_encode(array('result' => 'ok', 'objectId'  => 'foo_normalized')), $response->getContent());
         $this->assertEquals(array(), $this->session->getFlashBag()->all());
     }
 
@@ -1495,6 +1576,64 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals('SonataAdminBundle:CRUD:edit.html.twig', $this->template);
     }
 
+    /**
+     * @dataProvider getToStringValues
+     */
+    public function testEditActionWithModelManagerException($expectedToStringValue, $toStringValue)
+    {
+        $object = new \stdClass();
+
+        $this->admin->expects($this->once())
+            ->method('getObject')
+            ->will($this->returnValue($object));
+
+        $this->admin->expects($this->once())
+            ->method('isGranted')
+            ->with($this->equalTo('EDIT'))
+            ->will($this->returnValue(true));
+
+        $form = $this->getMockBuilder('Symfony\Component\Form\Form')
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->admin->expects($this->once())
+            ->method('getForm')
+            ->will($this->returnValue($form));
+
+        $form->expects($this->once())
+            ->method('isValid')
+            ->will($this->returnValue(true));
+
+        $this->admin->expects($this->once())
+            ->method('toString')
+            ->with($this->equalTo($object))
+            ->will($this->returnValue($toStringValue));
+
+        $this->expectTranslate('flash_edit_error', array('%name%' => $expectedToStringValue), 'SonataAdminBundle');
+
+        $this->request->setMethod('POST');
+
+        $formView = $this->getMock('Symfony\Component\Form\FormView');
+
+        $form->expects($this->any())
+            ->method('createView')
+            ->will($this->returnValue($formView));
+
+        $this->assertLoggerLogsModelManagerException($this->admin, 'update');
+        $this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $this->controller->editAction(null, $this->request));
+
+        $this->assertEquals($this->admin, $this->parameters['admin']);
+        $this->assertEquals('SonataAdminBundle::standard_layout.html.twig', $this->parameters['base_template']);
+        $this->assertEquals($this->pool, $this->parameters['admin_pool']);
+
+        $this->assertEquals('edit', $this->parameters['action']);
+        $this->assertInstanceOf('Symfony\Component\Form\FormView', $this->parameters['form']);
+        $this->assertEquals($object, $this->parameters['object']);
+
+        $this->assertEquals(array('sonata_flash_error' => array('flash_edit_error')), $this->session->getFlashBag()->all());
+        $this->assertEquals('SonataAdminBundle:CRUD:edit.html.twig', $this->template);
+    }
+
     public function testEditActionWithPreview()
     {
         $object = new \stdClass();
@@ -1559,6 +1698,28 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
         $this->controller->createAction($this->request);
     }
 
+    public function testPreCreate()
+    {
+        $object = new \stdClass();
+        $object->foo = 123456;
+
+        $this->admin->expects($this->once())
+            ->method('isGranted')
+            ->with($this->equalTo('CREATE'))
+            ->will($this->returnValue(true));
+
+        $this->admin->expects($this->once())
+            ->method('getNewInstance')
+            ->will($this->returnValue($object));
+
+        $controller = new PreCRUDController();
+        $controller->setContainer($this->container);
+
+        $response = $controller->createAction($this->request);
+        $this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response);
+        $this->assertEquals('preCreate called: 123456', $response->getContent());
+    }
+
     public function testCreateAction()
     {
         $this->admin->expects($this->once())
@@ -1750,7 +1911,66 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
         $this->assertInstanceOf('Symfony\Component\Form\FormView', $this->parameters['form']);
         $this->assertEquals($object, $this->parameters['object']);
 
-        $this->assertEquals(array('sonata_flash_error'=>array('flash_create_error')), $this->session->getFlashBag()->all());
+        $this->assertEquals(array('sonata_flash_error' => array('flash_create_error')), $this->session->getFlashBag()->all());
+        $this->assertEquals('SonataAdminBundle:CRUD:edit.html.twig', $this->template);
+    }
+
+    /**
+     * @dataProvider getToStringValues
+     */
+    public function testCreateActionWithModelManagerException($expectedToStringValue, $toStringValue)
+    {
+        $this->admin->expects($this->exactly(2))
+            ->method('isGranted')
+            ->with($this->equalTo('CREATE'))
+            ->will($this->returnValue(true));
+
+        $object = new \stdClass();
+
+        $this->admin->expects($this->once())
+            ->method('getNewInstance')
+            ->will($this->returnValue($object));
+
+        $form = $this->getMockBuilder('Symfony\Component\Form\Form')
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->admin->expects($this->once())
+            ->method('getForm')
+            ->will($this->returnValue($form));
+
+        $form->expects($this->once())
+            ->method('isValid')
+            ->will($this->returnValue(true));
+
+        $this->admin->expects($this->once())
+            ->method('toString')
+            ->with($this->equalTo($object))
+            ->will($this->returnValue($toStringValue));
+
+        $this->expectTranslate('flash_create_error', array('%name%' => $expectedToStringValue), 'SonataAdminBundle');
+
+        $this->request->setMethod('POST');
+
+        $formView = $this->getMock('Symfony\Component\Form\FormView');
+
+        $form->expects($this->any())
+            ->method('createView')
+            ->will($this->returnValue($formView));
+
+        $this->assertLoggerLogsModelManagerException($this->admin, 'create');
+
+        $this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $this->controller->createAction($this->request));
+
+        $this->assertEquals($this->admin, $this->parameters['admin']);
+        $this->assertEquals('SonataAdminBundle::standard_layout.html.twig', $this->parameters['base_template']);
+        $this->assertEquals($this->pool, $this->parameters['admin_pool']);
+
+        $this->assertEquals('create', $this->parameters['action']);
+        $this->assertInstanceOf('Symfony\Component\Form\FormView', $this->parameters['form']);
+        $this->assertEquals($object, $this->parameters['object']);
+
+        $this->assertEquals(array('sonata_flash_error' => array('flash_create_error')), $this->session->getFlashBag()->all());
         $this->assertEquals('SonataAdminBundle:CRUD:edit.html.twig', $this->template);
     }
 
@@ -1803,7 +2023,7 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
         $response = $this->controller->createAction($this->request);
 
         $this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response);
-        $this->assertEquals(json_encode(array('result'=>'ok', 'objectId'  => 'foo_normalized')), $response->getContent());
+        $this->assertEquals(json_encode(array('result' => 'ok', 'objectId'  => 'foo_normalized')), $response->getContent());
         $this->assertEquals(array(), $this->session->getFlashBag()->all());
     }
 
@@ -2769,7 +2989,7 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
             ->will($this->returnValue($batchActions));
 
         $this->request->setMethod('POST');
-        $this->request->request->set('data', json_encode(array('action'=>'foo', 'idx'=>array('123', '456'), 'all_elements'=>false)));
+        $this->request->request->set('data', json_encode(array('action' => 'foo', 'idx' => array('123', '456'), 'all_elements' => false)));
         $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.batch');
 
         $this->controller->batchAction($this->request);
@@ -2778,7 +2998,7 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
     public function testBatchActionActionInvalidCsrfToken()
     {
         $this->request->setMethod('POST');
-        $this->request->request->set('data', json_encode(array('action'=>'foo', 'idx'=>array('123', '456'), 'all_elements'=>false)));
+        $this->request->request->set('data', json_encode(array('action' => 'foo', 'idx' => array('123', '456'), 'all_elements' => false)));
         $this->request->request->set('_sonata_csrf_token', 'CSRF-INVALID');
 
         try {
@@ -2793,7 +3013,7 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
     {
         $this->setExpectedException('RuntimeException', 'A `Sonata\AdminBundle\Controller\CRUDController::batchActionFoo` method must be callable');
 
-        $batchActions = array('foo'=>array('label'=>'Foo Bar', 'ask_confirmation' => false));
+        $batchActions = array('foo' => array('label' => 'Foo Bar', 'ask_confirmation' => false));
 
         $this->admin->expects($this->once())
             ->method('getBatchActions')
@@ -2805,7 +3025,7 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
             ->will($this->returnValue($datagrid));
 
         $this->request->setMethod('POST');
-        $this->request->request->set('data', json_encode(array('action'=>'foo', 'idx'=>array('123', '456'), 'all_elements'=>false)));
+        $this->request->request->set('data', json_encode(array('action' => 'foo', 'idx' => array('123', '456'), 'all_elements' => false)));
         $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.batch');
 
         $this->controller->batchAction($this->request);
@@ -2813,7 +3033,7 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
 
     public function testBatchActionWithoutConfirmation()
     {
-        $batchActions = array('delete'=>array('label'=>'Foo Bar', 'ask_confirmation' => false));
+        $batchActions = array('delete' => array('label' => 'Foo Bar', 'ask_confirmation' => false));
 
         $this->admin->expects($this->once())
             ->method('getBatchActions')
@@ -2851,7 +3071,7 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
             ->will($this->returnValue(true));
 
         $this->request->setMethod('POST');
-        $this->request->request->set('data', json_encode(array('action'=>'delete', 'idx'=>array('123', '456'), 'all_elements'=>false)));
+        $this->request->request->set('data', json_encode(array('action' => 'delete', 'idx' => array('123', '456'), 'all_elements' => false)));
         $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.batch');
 
         $result = $this->controller->batchAction($this->request);
@@ -2863,7 +3083,7 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
 
     public function testBatchActionWithoutConfirmation2()
     {
-        $batchActions = array('delete'=>array('label'=>'Foo Bar', 'ask_confirmation' => false));
+        $batchActions = array('delete' => array('label' => 'Foo Bar', 'ask_confirmation' => false));
 
         $this->admin->expects($this->once())
             ->method('getBatchActions')
@@ -2914,13 +3134,13 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
 
     public function testBatchActionWithConfirmation()
     {
-        $batchActions = array('delete'=>array('label'=>'Foo Bar', 'ask_confirmation' => true));
+        $batchActions = array('delete' => array('label' => 'Foo Bar', 'ask_confirmation' => true));
 
         $this->admin->expects($this->once())
             ->method('getBatchActions')
             ->will($this->returnValue($batchActions));
 
-        $data = array('action'=>'delete', 'idx'=>array('123', '456'), 'all_elements'=>false);
+        $data = array('action' => 'delete', 'idx' => array('123', '456'), 'all_elements' => false);
 
         $this->request->setMethod('POST');
         $this->request->request->set('data', json_encode($data));
@@ -2936,7 +3156,7 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
             ->disableOriginalConstructor()
             ->getMock();
 
-         $form->expects($this->once())
+        $form->expects($this->once())
             ->method('createView')
             ->will($this->returnValue($this->getMock('Symfony\Component\Form\FormView')));
 
@@ -2966,7 +3186,7 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
         $controller = new BatchAdminController();
         $controller->setContainer($this->container);
 
-        $batchActions = array('foo'=>array('label'=>'Foo Bar', 'ask_confirmation' => false));
+        $batchActions = array('foo' => array('label' => 'Foo Bar', 'ask_confirmation' => false));
 
         $this->admin->expects($this->once())
             ->method('getBatchActions')
@@ -2995,7 +3215,7 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
         $controller = new BatchAdminController();
         $controller->setContainer($this->container);
 
-        $batchActions = array('foo'=>array('label'=>'Foo Bar', 'ask_confirmation' => false));
+        $batchActions = array('foo' => array('label' => 'Foo Bar', 'ask_confirmation' => false));
 
         $this->admin->expects($this->once())
             ->method('getBatchActions')
@@ -3021,7 +3241,7 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
 
     public function testBatchActionNoItems()
     {
-        $batchActions = array('delete'=>array('label'=>'Foo Bar', 'ask_confirmation' => true));
+        $batchActions = array('delete' => array('label' => 'Foo Bar', 'ask_confirmation' => true));
 
         $this->admin->expects($this->once())
             ->method('getBatchActions')
@@ -3050,7 +3270,7 @@ class CRUDControllerTest extends \PHPUnit_Framework_TestCase
         $controller = new BatchAdminController();
         $controller->setContainer($this->container);
 
-        $batchActions = array('bar'=>array('label'=>'Foo Bar', 'ask_confirmation' => false));
+        $batchActions = array('bar' => array('label' => 'Foo Bar', 'ask_confirmation' => false));
 
         $this->admin->expects($this->once())
             ->method('getBatchActions')

+ 40 - 0
Tests/Fixtures/Controller/PreCRUDController.php

@@ -0,0 +1,40 @@
+<?php
+
+namespace Sonata\AdminBundle\Tests\Fixtures\Controller;
+
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpFoundation\Request;
+use Sonata\AdminBundle\Controller\CRUDController;
+
+/**
+ * PreCRUDController
+ *
+ * @author Andrej Hudec <pulzarraider@gmail.com>
+ */
+class PreCRUDController extends CRUDController
+{
+    protected function preCreate(Request $request, $object)
+    {
+        return new Response(sprintf('preCreate called: %s', $object->foo));
+    }
+
+    protected function preEdit(Request $request, $object)
+    {
+        return new Response(sprintf('preEdit called: %s', $object->foo));
+    }
+
+    protected function preDelete(Request $request, $object)
+    {
+        return new Response(sprintf('preDelete called: %s', $object->foo));
+    }
+
+    protected function preShow(Request $request, $object)
+    {
+        return new Response(sprintf('preShow called: %s', $object->foo));
+    }
+
+    protected function preList(Request $request)
+    {
+        return new Response(sprintf('preList called'));
+    }
+}