diff --git a/Datatable/AbstractDatatable.php b/Datatable/AbstractDatatable.php index 49483ae5..d05c03eb 100755 --- a/Datatable/AbstractDatatable.php +++ b/Datatable/AbstractDatatable.php @@ -12,7 +12,7 @@ namespace Sg\DatatablesBundle\Datatable; use Doctrine\ORM\EntityManagerInterface; -use Exception; +use LogicException; use Sg\DatatablesBundle\Datatable\Column\ColumnBuilder; use Symfony\Component\PropertyAccess\PropertyAccess; use Symfony\Component\PropertyAccess\PropertyAccessor; @@ -147,7 +147,7 @@ abstract class AbstractDatatable implements DatatableInterface protected static $uniqueCounter = []; /** - * @throws Exception + * @throws LogicException */ public function __construct( AuthorizationCheckerInterface $authorizationChecker, @@ -313,12 +313,13 @@ public function getUniqueName() /** * Checks the name only contains letters, numbers, underscores or dashes. * - * @throws Exception + * @throws LogicException */ private function validateName() { - if (1 !== preg_match(self::NAME_REGEX, $this->getName())) { - throw new Exception('AbstractDatatable::validateName(): The result of the getName method can only contain letters, numbers, underscore and dashes.'); + $name = $this->getName(); + if (1 !== preg_match(self::NAME_REGEX, $name)) { + throw new LogicException(sprintf('AbstractDatatable::validateName(): "%s" is invalid Datatable Name. Name can only contain letters, numbers, underscore and dashes.', $name)); } } } diff --git a/Datatable/DatatableInterface.php b/Datatable/DatatableInterface.php index 0f3f18a7..d5192f7f 100644 --- a/Datatable/DatatableInterface.php +++ b/Datatable/DatatableInterface.php @@ -19,7 +19,7 @@ */ interface DatatableInterface { - const NAME_REGEX = '/[a-zA-Z0-9\-\_]+/'; + const NAME_REGEX = '/^[a-zA-Z0-9\-\_]+$/'; /** * Builds the datatable. diff --git a/Resources/doc/installation.md b/Resources/doc/installation.md index edb2ed7a..7cb98963 100644 --- a/Resources/doc/installation.md +++ b/Resources/doc/installation.md @@ -359,6 +359,11 @@ class PostDatatable extends AbstractDatatable } ``` +**Important:** +When declaring datatable "by hand" as extending `AbstractDatatable` class watch out for datatable name as returned from +`getName()` method. Valid datatable name may contains only letters ([a-zA-Z]), digits (0-9), dashes (-) and underscores +(_). Putting any other character in name will cause `\LogicException` to be thrown. + ### Step 2: (Optional) Registering your Datatable as a Service ``` yaml diff --git a/Tests/DatatableTest.php b/Tests/DatatableTest.php index 1486d20d..4b934465 100755 --- a/Tests/DatatableTest.php +++ b/Tests/DatatableTest.php @@ -12,6 +12,8 @@ namespace Sg\DatatablesBundle\Tests; use Doctrine\ORM\EntityManager; +use ReflectionClass; +use Sg\DatatablesBundle\Datatable\AbstractDatatable; use Sg\DatatablesBundle\Tests\Datatables\PostDatatable; use Symfony\Component\Routing\RouterInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; @@ -64,6 +66,50 @@ public function testCreate() $table->buildDatatable(); } + public function testInvalidName() + { + /** @noinspection PhpUndefinedMethodInspection */ + $authorizationChecker = $this->createMock(AuthorizationCheckerInterface::class); + /** @noinspection PhpUndefinedMethodInspection */ + $securityToken = $this->createMock(TokenStorageInterface::class); + /** @noinspection PhpUndefinedMethodInspection */ + $translator = $this->createMock(TranslatorInterface::class); + /** @noinspection PhpUndefinedMethodInspection */ + $router = $this->createMock(RouterInterface::class); + /** @noinspection PhpUndefinedMethodInspection */ + $twig = $this->createMock(Environment::class); + + /** @noinspection PhpUndefinedMethodInspection */ + $em = $this->getMockBuilder(EntityManager::class) + ->disableOriginalConstructor() + ->setMethods( + ['getClassMetadata'] + ) + ->getMock() + ; + + // @noinspection PhpUndefinedMethodInspection + $em->expects(static::any()) + ->method('getClassMetadata') + ->willReturn($this->getClassMetadataMock()) + ; + + $mock = $this->getMockBuilder(AbstractDatatable::class) + ->disableOriginalConstructor() + ->setMethods(['getName']) + ->getMockForAbstractClass() + ; + $mock->expects(static::any()) + ->method('getName') + ->willReturn('invalid.name') + ; + + $refledtionClass = new ReflectionClass(AbstractDatatable::class); + $constructor = $refledtionClass->getConstructor(); + $this->expectException(\LogicException::class); + $constructor->invoke($mock, $authorizationChecker, $securityToken, $translator, $router, $em, $twig); + } + public function getClassMetadataMock() { /** @noinspection PhpUndefinedMethodInspection */