diff --git a/README.md b/README.md index b0d610bd..2e06b327 100644 --- a/README.md +++ b/README.md @@ -337,54 +337,69 @@ You can then use the `Slugify::slugify()` method in your controllers: $url = Slugify::slugify("welcome to the homepage"); ``` -### Zend Framework 2 +### Mezzio and laminas-mvc -Slugify can be easely used in Zend Framework 2 applications. Included bridge provides a service and a view helper +Slugify can be easily used in Mezzio or laminas-mvc applications. Included bridge provides a filter and a view helper already registered for you. Just enable the module in your configuration like this. ```php return [ - //... - - "modules" => [ - "Application", - "ZfcBase", - "Cocur\Slugify\Bridge\ZF2", // <- Add this line + 'modules' => [ + 'Application', + 'Cocur\Slugify\Bridge\Laminas' // <- Add this line //... - ], - - //... + ] ]; ``` - +It will automatically inject the config-provider or the module in the configuration during the installation process. After that you can retrieve the `Cocur\Slugify\Slugify` service (or the `slugify` alias) and generate a slug. ```php -/** @var \Zend\ServiceManager\ServiceManager $sm */ -$slugify = $sm->get("Cocur\Slugify\Slugify"); -$slug = $slugify->slugify("Hällo Wörld"); -$anotherSlug = $slugify->slugify("Hällo Wörld", "_"); +/** @var \Laminas\ServiceManager\ServiceManager $sm */ +$slugify = $sm->get('Cocur\Slugify\Slugify'); +$slug = $slugify->slugify('Hällo Wörld'); +$anotherSlug = $slugify->slugify('Hällo Wörld', '_'); +``` + +It can be used in form filters as follows. + +```php +'my_form_input' => [ + 'filters' => [ + [ + 'name' => SlugifyFilter::class, + 'options' => [ + 'regexp' => Slugify::LOWERCASE_NUMBERS_DASHES, + 'strip_tags' => true, + //... + ] + ], + ], + //... +], +//... ``` In your view templates use the `slugify` helper to generate slugs. ```php -slugify("Hällo Wörld"); ?> -slugify("Hällo Wörld", "_"); ?> +slugify('Hällo Wörld') ?> +slugify('Hällo Wörld', '_') ?> ``` The service (which is also used in the view helper) can be customized by defining this configuration key. ```php return [ - "cocur_slugify" => [ - "reg_exp" => "/([^a-zA-Z0-9]|-)+/", - ], + 'cocur_slugify' => [ + 'reg_exp' => '/([^a-zA-Z0-9]|-)+/' + ] ]; ``` + ### Nette Framework Slugify contains a Nette extension that allows you to use it as a service in your Nette application. You only need to diff --git a/composer.json b/composer.json index c6ae6aef..a11a36a8 100644 --- a/composer.json +++ b/composer.json @@ -22,6 +22,12 @@ "php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0", "ext-mbstring": "*" }, + "extra": { + "laminas": { + "config-provider": "Cocur\\Slugify\\Bridge\\Laminas\\ConfigProvider", + "module": "Cocur\\Slugify\\Bridge\\Laminas" + } + }, "conflict": { "symfony/config": "<3.4 || >=4,<4.3", "symfony/dependency-injection": "<3.4 || >=4,<4.3", @@ -42,9 +48,8 @@ "symfony/http-kernel": "^3.4 || ^4.3 || ^5.0 || ^6.0", "symfony/phpunit-bridge": "^5.4 || ^6.0", "twig/twig": "^2.12.1 || ~3.0", - "zendframework/zend-modulemanager": "~2.2", - "zendframework/zend-servicemanager": "~2.2", - "zendframework/zend-view": "~2.2" + "laminas/laminas-view": "~2.9", + "laminas/laminas-filter": "^2.31" }, "autoload": { "psr-4": { diff --git a/src/Bridge/Laminas/ConfigProvider.php b/src/Bridge/Laminas/ConfigProvider.php new file mode 100644 index 00000000..cd75ade0 --- /dev/null +++ b/src/Bridge/Laminas/ConfigProvider.php @@ -0,0 +1,72 @@ + $this->getDependencyConfig(), + 'filters' => $this->filterConfig(), + 'view_helpers' => $this->getViewHelperConfig(), + ]; + } + + /** + * Retrieve laminas default dependency configuration. + * + * @return array + */ + public function getDependencyConfig(): array + { + return [ + 'factories' => [ + Slugify::class => SlugifyService::class, + ], + 'aliases' => [ + 'slugify' => Slugify::class, + ] + ]; + } + + /** + * Retrieve laminas view helper dependency configuration. + * + * @return array + */ + public function getViewHelperConfig(): array + { + return [ + 'aliases' => [ + 'slugify' => SlugifyViewHelper::class + ], + 'factories' => [ + SlugifyViewHelper::class => SlugifyViewHelperFactory::class + ] + ]; + } + + /** + * @return array + */ + private function filterConfig(): array + { + return [ + 'factories' => [ + SlugifyFilter::class => InvokableFactory::class, + ], + 'aliases' => [ + 'slugify' => SlugifyFilter::class, + ], + ]; + } +} diff --git a/src/Bridge/Laminas/Module.php b/src/Bridge/Laminas/Module.php new file mode 100644 index 00000000..5940a9ed --- /dev/null +++ b/src/Bridge/Laminas/Module.php @@ -0,0 +1,24 @@ + Slugify::LOWERCASE_NUMBERS_DASHES, + 'separator' => '-', + 'lowercase' => true, + 'lowercase_after_regexp' => false, + 'trim' => true, + 'strip_tags' => false + ]; + + /** + * @param array $options + */ + public function __construct(array $options = []) + { + if (!empty($options)) { + $this->setOptions($options); + } + } + + /** + * Returns the result of filtering $value + * + * @param mixed $value + * + * @return mixed + */ + public function filter($value) + { + if (!empty($value)) { + $slugify = new Slugify($this->options); + return $slugify->slugify((string) $value); + } + + return $value; + } + + /** + * @param array $options + * + * @return void + */ + protected function setOptions(array $options) + { + foreach ($options as $key => $option) { + if (array_key_exists($key, $this->options)) { + $this->options[$key] = $option; + } + } + } +} diff --git a/src/Bridge/Laminas/SlugifyService.php b/src/Bridge/Laminas/SlugifyService.php new file mode 100644 index 00000000..b6972e2b --- /dev/null +++ b/src/Bridge/Laminas/SlugifyService.php @@ -0,0 +1,35 @@ +get('Config'); + + $slugifyOptions = $config[Module::CONFIG_KEY]['options'] ?? []; + $provider = $config[Module::CONFIG_KEY]['provider'] ?? null; + + return new Slugify($slugifyOptions, $provider); + } + +} diff --git a/src/Bridge/ZF2/SlugifyViewHelper.php b/src/Bridge/Laminas/SlugifyViewHelper.php similarity index 83% rename from src/Bridge/ZF2/SlugifyViewHelper.php rename to src/Bridge/Laminas/SlugifyViewHelper.php index ef51a879..d6ddff93 100644 --- a/src/Bridge/ZF2/SlugifyViewHelper.php +++ b/src/Bridge/Laminas/SlugifyViewHelper.php @@ -1,9 +1,9 @@ slugify->slugify($string, $separator); } diff --git a/src/Bridge/Laminas/SlugifyViewHelperFactory.php b/src/Bridge/Laminas/SlugifyViewHelperFactory.php new file mode 100644 index 00000000..6f6b65f4 --- /dev/null +++ b/src/Bridge/Laminas/SlugifyViewHelperFactory.php @@ -0,0 +1,32 @@ +get('Cocur\Slugify\Slugify'); + + return new SlugifyViewHelper($slugify); + } + +} diff --git a/src/Bridge/ZF2/Module.php b/src/Bridge/ZF2/Module.php deleted file mode 100644 index 5babfc2a..00000000 --- a/src/Bridge/ZF2/Module.php +++ /dev/null @@ -1,50 +0,0 @@ -> - */ - public function getServiceConfig(): array - { - return [ - 'factories' => [ - 'Cocur\Slugify\Slugify' => 'Cocur\Slugify\Bridge\ZF2\SlugifyService' - ], - 'aliases' => [ - 'slugify' => 'Cocur\Slugify\Slugify' - ] - ]; - } - - /** - * Expected to return \Zend\ServiceManager\Config object or array to - * seed such an object. - * - * @return array>|\Zend\ServiceManager\Config - */ - public function getViewHelperConfig(): array - { - return [ - 'factories' => [ - 'slugify' => 'Cocur\Slugify\Bridge\ZF2\SlugifyViewHelperFactory' - ] - ]; - } -} diff --git a/src/Bridge/ZF2/SlugifyService.php b/src/Bridge/ZF2/SlugifyService.php deleted file mode 100644 index 9d7d45aa..00000000 --- a/src/Bridge/ZF2/SlugifyService.php +++ /dev/null @@ -1,30 +0,0 @@ -get('Config'); - - $options = isset($config[Module::CONFIG_KEY]['options']) ? $config[Module::CONFIG_KEY]['options'] : []; - $provider = isset($config[Module::CONFIG_KEY]['provider']) ? $config[Module::CONFIG_KEY]['provider'] : null; - - return new Slugify($options, $provider); - } -} diff --git a/src/Bridge/ZF2/SlugifyViewHelperFactory.php b/src/Bridge/ZF2/SlugifyViewHelperFactory.php deleted file mode 100644 index 878fec19..00000000 --- a/src/Bridge/ZF2/SlugifyViewHelperFactory.php +++ /dev/null @@ -1,28 +0,0 @@ -getServiceLocator()->get(Slugify::class); - - return new SlugifyViewHelper($slugify); - } -} diff --git a/tests/Bridge/Laminas/ModuleTest.php b/tests/Bridge/Laminas/ModuleTest.php new file mode 100644 index 00000000..8e3a20d4 --- /dev/null +++ b/tests/Bridge/Laminas/ModuleTest.php @@ -0,0 +1,55 @@ +module = new Module(); + } + + /** + * @covers \Cocur\Slugify\Bridge\Laminas\Module::getConfig() + * @covers \Cocur\Slugify\Bridge\Laminas\ConfigProvider::getDependencyConfig() + */ + public function testGetServiceConfig() + { + $smConfig = $this->module->getConfig(); + $this->assertIsArray($smConfig); + $this->assertArrayHasKey('service_manager', $smConfig); + $this->assertArrayHasKey('factories', $smConfig['service_manager']); + $this->assertArrayHasKey('Cocur\Slugify\Slugify', $smConfig['service_manager']['factories']); + $this->assertArrayHasKey('aliases', $smConfig['service_manager']); + $this->assertArrayHasKey('slugify', $smConfig['service_manager']['aliases']); + } + + /** + * @covers \Cocur\Slugify\Bridge\Laminas\Module::getConfig() + * @covers \Cocur\Slugify\Bridge\Laminas\ConfigProvider::getViewHelperConfig() + */ + public function testGetViewHelperConfig() + { + $vhConfig = $this->module->getConfig(); + $this->assertIsArray($vhConfig); + $this->assertArrayHasKey('view_helpers', $vhConfig); + $this->assertArrayHasKey('factories', $vhConfig['view_helpers']); + $this->assertArrayHasKey(SlugifyViewHelper::class, $vhConfig['view_helpers']['factories']); + $this->assertArrayHasKey('aliases', $vhConfig['view_helpers']); + $this->assertArrayHasKey('slugify', $vhConfig['view_helpers']['aliases']); + } +} diff --git a/tests/Bridge/Laminas/SlugifyFilterTest.php b/tests/Bridge/Laminas/SlugifyFilterTest.php new file mode 100644 index 00000000..10946a49 --- /dev/null +++ b/tests/Bridge/Laminas/SlugifyFilterTest.php @@ -0,0 +1,66 @@ + [ + [ + 'name' => SlugifyFilter::class, + ], + ], + ] + ); + + $value = 'foo Bar'; + $expected = 'foo-bar'; + + $this->assertSame($expected, $chain->filter($value)); + + } + + /** + * @covers \Cocur\Slugify\Bridge\Laminas\SlugifyFilter::filter() + */ + public function testSlugifyFilterWithCustomOptions() + { + $chain = new FilterChain( + [ + 'filters' => [ + [ + 'name' => SlugifyFilter::class, + 'options' => [ + 'regexp' => '/([^0-9test\/]|-)+/', + 'strip_tags' => false, + ] + ], + ], + ] + ); + + $value = '0123 foo bar '; + $expected = '0123-test'; + + $this->assertSame($expected, $chain->filter($value)); + } + +} diff --git a/tests/Bridge/ZF2/SlugifyServiceTest.php b/tests/Bridge/Laminas/SlugifyServiceTest.php similarity index 74% rename from tests/Bridge/ZF2/SlugifyServiceTest.php rename to tests/Bridge/Laminas/SlugifyServiceTest.php index 13cbfbc6..7999d350 100644 --- a/tests/Bridge/ZF2/SlugifyServiceTest.php +++ b/tests/Bridge/Laminas/SlugifyServiceTest.php @@ -1,9 +1,10 @@ slugifyService = new SlugifyService(); } /** - * @covers \Cocur\Slugify\Bridge\ZF2\SlugifyService::__invoke() + * @covers \Cocur\Slugify\Bridge\Laminas\SlugifyService::__invoke() */ public function testInvokeWithoutCustomConfig() { @@ -40,7 +42,7 @@ public function testInvokeWithoutCustomConfig() } /** - * @covers \Cocur\Slugify\Bridge\ZF2\SlugifyService::__invoke() + * @covers \Cocur\Slugify\Bridge\Laminas\SlugifyService::__invoke() */ public function testInvokeWithCustomConfig() { @@ -52,15 +54,20 @@ public function testInvokeWithCustomConfig() $slugify = call_user_func($this->slugifyService, $sm); $this->assertInstanceOf('Cocur\Slugify\Slugify', $slugify); - // Make sure reg exp is the one provided and dots are kept + // Make sure regexp is the one provided and dots are kept $actual = 'Hello My Friend.zip'; $expected = 'hello-my-friend.zip'; $this->assertSame($expected, $slugify->slugify($actual)); } - protected function createServiceManagerMock(array $config = []) + /** + * @param array $config + * + * @return ServiceManager + */ + protected function createServiceManagerMock(array $config = []): ServiceManager { - $sm = new ServiceManager(); + $sm = new ServiceManager($config); $sm->setService('Config', $config); return $sm; diff --git a/tests/Bridge/ZF2/SlugifyViewHelperFactoryTest.php b/tests/Bridge/Laminas/SlugifyViewHelperFactoryTest.php similarity index 57% rename from tests/Bridge/ZF2/SlugifyViewHelperFactoryTest.php rename to tests/Bridge/Laminas/SlugifyViewHelperFactoryTest.php index 54356a47..c0244ab2 100644 --- a/tests/Bridge/ZF2/SlugifyViewHelperFactoryTest.php +++ b/tests/Bridge/Laminas/SlugifyViewHelperFactoryTest.php @@ -1,10 +1,9 @@ setService('Cocur\Slugify\Slugify', new Slugify()); - $vhm = new HelperPluginManager(); - $vhm->setServiceLocator($sm); - $viewHelper = call_user_func($this->factory, $vhm); - $this->assertInstanceOf('Cocur\Slugify\Bridge\ZF2\SlugifyViewHelper', $viewHelper); + $viewHelper = call_user_func($this->factory, $sm); + $this->assertInstanceOf('Cocur\Slugify\Bridge\Laminas\SlugifyViewHelper', $viewHelper); } } diff --git a/tests/Bridge/ZF2/SlugifyViewHelperTest.php b/tests/Bridge/Laminas/SlugifyViewHelperTest.php similarity index 79% rename from tests/Bridge/ZF2/SlugifyViewHelperTest.php rename to tests/Bridge/Laminas/SlugifyViewHelperTest.php index a037dee4..fa3e376f 100644 --- a/tests/Bridge/ZF2/SlugifyViewHelperTest.php +++ b/tests/Bridge/Laminas/SlugifyViewHelperTest.php @@ -1,7 +1,7 @@ module = new Module(); - } - - /** - * @covers \Cocur\Slugify\Bridge\ZF2\Module::getServiceConfig() - */ - public function testGetServiceConfig() - { - $smConfig = $this->module->getServiceConfig(); - $this->assertIsArray($smConfig); - $this->assertArrayHasKey('factories', $smConfig); - $this->assertArrayHasKey('Cocur\Slugify\Slugify', $smConfig['factories']); - $this->assertArrayHasKey('aliases', $smConfig); - $this->assertArrayHasKey('slugify', $smConfig['aliases']); - } - - /** - * @covers \Cocur\Slugify\Bridge\ZF2\Module::getViewHelperConfig() - */ - public function testGetViewHelperConfig() - { - $vhConfig = $this->module->getViewHelperConfig(); - $this->assertIsArray($vhConfig); - $this->assertArrayHasKey('factories', $vhConfig); - $this->assertArrayHasKey('slugify', $vhConfig['factories']); - } -}