From e0138545f3313453a48b4e9f636f53cdb74594ed Mon Sep 17 00:00:00 2001 From: ADmad Date: Wed, 26 Apr 2023 22:21:52 +0530 Subject: [PATCH 1/6] Upgrade for Cake 5. --- .github/workflows/ci.yml | 117 +++--------------- composer.json | 18 ++- phpunit.xml.dist | 35 +++--- psalm.xml | 2 + src/Model/Behavior/SlugBehavior.php | 63 ++++------ src/{Plugin.php => SlugPlugin.php} | 10 +- src/Slugger/CocurSlugger.php | 6 +- tests/Fixture/ArticlesFixture.php | 20 +-- tests/Fixture/ArticlesTagsFixture.php | 10 +- tests/Fixture/AuthorsFixture.php | 15 +-- tests/Fixture/TagsFixture.php | 17 +-- .../Model/Behavior/SlugBehaviorTest.php | 22 ++-- tests/schema.php | 45 +++++++ 13 files changed, 141 insertions(+), 239 deletions(-) rename src/{Plugin.php => SlugPlugin.php} (66%) create mode 100644 tests/schema.php diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fad9335..356a5ff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,108 +1,21 @@ name: CI -on: [push, pull_request] +on: + push: + branches: + - master + pull_request: + branches: + - '*' + +permissions: + contents: read jobs: testsuite: - runs-on: ubuntu-18.04 - strategy: - fail-fast: false - matrix: - php-version: ['7.2', '8.0', '8.1'] - db-type: [sqlite, mysql, pgsql] - prefer-lowest: [''] - include: - - php-version: '7.2' - db-type: 'sqlite' - prefer-lowest: 'prefer-lowest' - - services: - postgres: - image: postgres - ports: - - 5432:5432 - env: - POSTGRES_PASSWORD: postgres - - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 1 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php-version }} - extensions: mbstring, intl, pdo_${{ matrix.db-type }} - coverage: pcov - - - name: Composer Install - run: composer install - - - name: Run PHPUnit - run: | - if [ ${{ matrix.db-type }} == 'mysql' ]; then - sudo service mysql start - mysql -h 127.0.0.1 -u root -proot -e 'CREATE DATABASE cakephp;' - fi - - if [[ ${{ matrix.db-type }} == 'sqlite' ]]; then export DB_DSN='sqlite:///:memory:'; fi - if [[ ${{ matrix.db-type }} == 'mysql' ]]; then export DB_DSN='mysql://root:root@127.0.0.1/cakephp'; fi - if [[ ${{ matrix.db-type }} == 'pgsql' ]]; then export DB_DSN='postgres://postgres:postgres@127.0.0.1/postgres'; fi - - if [[ ${{ matrix.php-version }} == '7.2' && ${{ matrix.db-type }} == 'sqlite' ]]; then - vendor/bin/phpunit --coverage-clover=coverage.xml - else - vendor/bin/phpunit - fi - - - name: Code Coverage Report - if: matrix.php-version == '7.2' && matrix.db-type == 'sqlite' - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - run: bash <(curl -s https://codecov.io/bash) - - coding-standard: - name: Coding Standard - runs-on: ubuntu-18.04 - - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 1 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: '7.2' - extensions: mbstring, intl - coverage: none - - - name: Composer Install - run: composer require --dev cakephp/cakephp-codesniffer:^4.0 - - - name: Run PHP CodeSniffer - run: vendor/bin/phpcs --standard=vendor/cakephp/cakephp-codesniffer/CakePHP -p src/ tests/ - - static-analysis: - name: Static Analysis - runs-on: ubuntu-18.04 - - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 1 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: '8.0' - extension-csv: mbstring, intl - coverage: none, - tools: vimeo/psalm:4.1 - - - name: Composer Install - run: composer install + uses: ADmad/.github/.github/workflows/testsuite-with-db.yml@master + secrets: inherit - - name: Run psalm - run: psalm + cs-stan: + uses: ADmad/.github/.github/workflows/cs-stan.yml@master + secrets: inherit diff --git a/composer.json b/composer.json index b37988d..1a3011c 100644 --- a/composer.json +++ b/composer.json @@ -31,12 +31,12 @@ "source": "https://github.com/usemuffin/slug" }, "require": { - "cakephp/orm": "^4.0" + "cakephp/orm": "5.x-dev" }, "require-dev": { - "cakephp/cakephp": "^4.0", - "phpunit/phpunit": "~8.5.0", - "cocur/slugify": "^1.2" + "cakephp/cakephp": "5.x-dev", + "phpunit/phpunit": "^9.6", + "cocur/slugify": "^4.3" }, "autoload": { "psr-4": { @@ -47,5 +47,13 @@ "psr-4": { "Muffin\\Slug\\Test\\": "tests" } - } + }, + "config": { + "sort-packages": true, + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true + } + }, + "minimum-stability": "dev", + "prefer-stable": true } diff --git a/phpunit.xml.dist b/phpunit.xml.dist index f90d715..be8879d 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,31 +1,28 @@ - - ./tests/ + + ./tests/TestCase - - - - - - - - + + + + + + + src/ + + - - - - ./src/ - - + + + diff --git a/psalm.xml b/psalm.xml index 9c6f41e..daca502 100644 --- a/psalm.xml +++ b/psalm.xml @@ -5,6 +5,8 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="https://getpsalm.org/schema/config" xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd" + findUnusedBaselineEntry="true" + findUnusedCode="false" > diff --git a/src/Model/Behavior/SlugBehavior.php b/src/Model/Behavior/SlugBehavior.php index c1b407f..abf0e7a 100644 --- a/src/Model/Behavior/SlugBehavior.php +++ b/src/Model/Behavior/SlugBehavior.php @@ -7,12 +7,11 @@ use Cake\Datasource\EntityInterface; use Cake\Event\EventInterface; use Cake\ORM\Behavior; -use Cake\ORM\Query; +use Cake\ORM\Query\SelectQuery; use Cake\ORM\Table; use Cake\Utility\Hash; use Cake\Utility\Text; use Cake\Validation\Validator; -use Closure; use InvalidArgumentException; use Muffin\Slug\Slugger\CakeSlugger; use Muffin\Slug\SluggerInterface; @@ -53,7 +52,7 @@ class SlugBehavior extends Behavior * * @var array */ - protected $_defaultConfig = [ + protected array $_defaultConfig = [ 'field' => 'slug', 'displayField' => null, 'separator' => '-', @@ -88,7 +87,7 @@ class SlugBehavior extends Behavior * * @var \Muffin\Slug\SluggerInterface */ - protected $_slugger; + protected SluggerInterface $_slugger; /** * Constructor. @@ -108,7 +107,7 @@ public function __construct(Table $table, array $config = []) /** * Initialize behavior * - * @param array $config The configuration settings provided to this behavior. + * @param array $config The configuration settings provided to this behavior. * @return void */ public function initialize(array $config): void @@ -126,7 +125,7 @@ public function initialize(array $config): void } if ($this->getConfig('unique') === true) { - $this->setConfig('unique', Closure::fromCallable([$this, '_uniqueSlug'])); + $this->setConfig('unique', $this->_uniqueSlug(...)); } } @@ -137,22 +136,18 @@ public function initialize(array $config): void */ public function getSlugger(): SluggerInterface { - if ($this->_slugger instanceof SluggerInterface) { - return $this->_slugger; - } - - return $this->_slugger = $this->_createSlugger($this->getConfig('slugger')); + /** @psalm-suppress RedundantPropertyInitializationCheck */ + return $this->_slugger ??= $this->_createSlugger($this->getConfig('slugger')); } /** * Set slugger instance. * - * @param \Muffin\Slug\SluggerInterface|string|array $slugger Sets slugger instance. + * @param \Muffin\Slug\SluggerInterface|class-string<\Muffin\Slug\SluggerInterface>|array $slugger Sets slugger instance. * Can be SluggerInterface instance or class name or config array. * @return void - * @psalm-param \Muffin\Slug\SluggerInterface|class-string|array $slugger */ - public function setSlugger($slugger): void + public function setSlugger(SluggerInterface|string|array $slugger): void { $this->_slugger = $this->_createSlugger($slugger); } @@ -160,30 +155,25 @@ public function setSlugger($slugger): void /** * Create slugger instance * - * @param \Muffin\Slug\SluggerInterface|string|array $slugger Sets slugger instance. + * @param \Muffin\Slug\SluggerInterface|class-string<\Muffin\Slug\SluggerInterface>|array $slugger Sets slugger instance. * Can be SluggerInterface instance or class name or config array. * @return \Muffin\Slug\SluggerInterface - * @psalm-param \Muffin\Slug\SluggerInterface|class-string|array $slugger * @psalm-suppress MoreSpecificReturnType */ - protected function _createSlugger($slugger): SluggerInterface + protected function _createSlugger(SluggerInterface|string|array $slugger): SluggerInterface { if (is_string($slugger)) { - /** - * @psalm-suppress LessSpecificReturnStatement - * @psalm-suppress InvalidStringClass - */ return new $slugger(); } if (is_array($slugger)) { + /** @var class-string<\Muffin\Slug\SluggerInterface> $className */ $className = $slugger['className']; unset($slugger['className']); - /** @psalm-suppress LessSpecificReturnStatement */ + return new $className($slugger); } - /** @var \Muffin\Slug\SluggerInterface */ return $slugger; } @@ -205,8 +195,9 @@ public function implementedEvents(): array * @param string $name Validator name. * @return void */ - public function buildValidator(EventInterface $event, Validator $validator, string $name) + public function buildValidator(EventInterface $event, Validator $validator, string $name): void { + /** @var string $field */ foreach ((array)$this->getConfig('displayField') as $field) { if (strpos($field, '.') === false) { $validator->requirePresence($field, 'create') @@ -223,7 +214,7 @@ public function buildValidator(EventInterface $event, Validator $validator, stri * @param \ArrayObject $options Options. * @return void */ - public function beforeSave(EventInterface $event, EntityInterface $entity, ArrayObject $options) + public function beforeSave(EventInterface $event, EntityInterface $entity, ArrayObject $options): void { $isNew = $entity->isNew(); if (!$isNew && !$this->getConfig('onUpdate')) { @@ -280,11 +271,11 @@ protected function _getPartsFromEntity(EntityInterface $entity): array /** * Custom finder. * - * @param \Cake\ORM\Query $query Query. + * @param \Cake\ORM\Query\SelectQuery $query Query. * @param array $options Options. - * @return \Cake\ORM\Query Query. + * @return \Cake\ORM\Query\SelectQuery Query. */ - public function findSlugged(Query $query, array $options): Query + public function findSlugged(SelectQuery $query, array $options): SelectQuery { if (!isset($options['slug'])) { throw new InvalidArgumentException('The `slug` key is required by the `slugged` finder.'); @@ -303,27 +294,23 @@ public function findSlugged(Query $query, array $options): Query * @param string|null $separator Separator. * @return string Slug. */ - public function slug($entity, ?string $string = null, ?string $separator = null): string + public function slug(EntityInterface|string $entity, ?string $string = null, ?string $separator = null): string { - if ($separator === null) { - $separator = $this->getConfig('separator'); - } + $separator ??= $this->getConfig('separator'); if (is_string($entity)) { if ($string !== null) { $separator = $string; } $string = $entity; - unset($entity); - } elseif (($entity instanceof EntityInterface) && $string === null) { + } elseif ($string === null) { $string = $this->_getSlugStringFromEntity($entity, $separator); } - /** @psalm-suppress PossiblyNullArgument */ $slug = $this->_slug($string, $separator); $unique = $this->getConfig('unique'); - if (isset($entity) && $unique) { + if (!is_string($entity) && $unique) { $slug = $unique($entity, $slug, $separator); } @@ -399,7 +386,7 @@ protected function _uniqueSlug(EntityInterface $entity, string $slug, string $se $i = 0; $suffix = ''; - $length = $this->getConfig('maxLength'); + $length = (int)$this->getConfig('maxLength'); while ($this->_table->exists($conditions)) { $i++; @@ -426,7 +413,7 @@ protected function _slug(string $string, string $separator): string $slugger = $this->getSlugger(); /** @psalm-suppress PossiblyNullReference */ $slug = $slugger->slug(str_replace(array_keys($replacements), $replacements, $string), $separator); - if (!empty($this->getConfig('maxLength'))) { + if ($this->getConfig('maxLength')) { $slug = Text::truncate( $slug, $this->getConfig('maxLength'), diff --git a/src/Plugin.php b/src/SlugPlugin.php similarity index 66% rename from src/Plugin.php rename to src/SlugPlugin.php index 028fece..be21e54 100644 --- a/src/Plugin.php +++ b/src/SlugPlugin.php @@ -10,28 +10,28 @@ class Plugin extends BasePlugin /** * The name of this plugin * - * @var string + * @var string|null */ - protected $name = 'Slug'; + protected ?string $name = 'Slug'; /** * Do bootstrapping or not * * @var bool */ - protected $bootstrapEnabled = false; + protected bool $bootstrapEnabled = false; /** * Load routes or not * * @var bool */ - protected $routesEnabled = false; + protected bool $routesEnabled = false; /** * Console middleware * * @var bool */ - protected $consoleEnabled = false; + protected bool $consoleEnabled = false; } diff --git a/src/Slugger/CocurSlugger.php b/src/Slugger/CocurSlugger.php index da72355..037b95f 100644 --- a/src/Slugger/CocurSlugger.php +++ b/src/Slugger/CocurSlugger.php @@ -44,10 +44,6 @@ public function __construct(array $config = []) */ public function slug(string $string, string $separator = '-'): string { - $options = $this->config; - $regex = $options['regex']; - unset($options['regex']); - - return Slugify::create($regex, $options)->slugify($string, $separator); + return Slugify::create($this->config)->slugify($string, $separator); } } diff --git a/tests/Fixture/ArticlesFixture.php b/tests/Fixture/ArticlesFixture.php index 63c9eeb..ce4b0b8 100644 --- a/tests/Fixture/ArticlesFixture.php +++ b/tests/Fixture/ArticlesFixture.php @@ -5,30 +5,14 @@ class ArticlesFixture extends TestFixture { - public $table = 'slug_articles'; - - /** - * fields property - * - * @var array - */ - public $fields = [ - 'id' => ['type' => 'integer'], - 'author_id' => ['type' => 'integer'], - 'title' => ['type' => 'string', 'null' => false], - 'sub_title' => ['type' => 'string', 'null' => false], - 'slug' => ['type' => 'string', 'null' => false], - 'created' => ['type' => 'datetime', 'null' => true], - 'modified' => ['type' => 'datetime', 'null' => true], - '_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]], - ]; + public string $table = 'slug_articles'; /** * records property * * @var array */ - public $records = [ + public array $records = [ ['author_id' => 1, 'title' => 'First Article', 'sub_title' => 'subtitle 1', 'slug' => 'first-title'], ['author_id' => 1, 'title' => 'Second Article', 'sub_title' => 'subtitle 2', 'slug' => 'second-title'], ['author_id' => 1, 'title' => 'Third Article', 'sub_title' => 'subtitle 3', 'slug' => 'third-title'], diff --git a/tests/Fixture/ArticlesTagsFixture.php b/tests/Fixture/ArticlesTagsFixture.php index e13f6a6..8286a1f 100644 --- a/tests/Fixture/ArticlesTagsFixture.php +++ b/tests/Fixture/ArticlesTagsFixture.php @@ -6,15 +6,9 @@ class ArticlesTagsFixture extends TestFixture { - public $table = 'slug_articles_tags'; + public string $table = 'slug_articles_tags'; - public $fields = [ - 'id' => ['type' => 'integer'], - 'article_id' => ['type' => 'integer'], - 'slug_tag_id' => ['type' => 'integer'], - ]; - - public $records = [ + public array $records = [ ['article_id' => 1, 'slug_tag_id' => 1], ['article_id' => 1, 'slug_tag_id' => 2], ['article_id' => 2, 'slug_tag_id' => 2], diff --git a/tests/Fixture/AuthorsFixture.php b/tests/Fixture/AuthorsFixture.php index e0773eb..de2adb2 100644 --- a/tests/Fixture/AuthorsFixture.php +++ b/tests/Fixture/AuthorsFixture.php @@ -5,25 +5,14 @@ class AuthorsFixture extends TestFixture { - public $table = 'slug_authors'; - - /** - * fields property - * - * @var array - */ - public $fields = [ - 'id' => ['type' => 'integer'], - 'name' => ['type' => 'string', 'null' => false], - '_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]], - ]; + public string $table = 'slug_authors'; /** * records property * * @var array */ - public $records = [ + public array $records = [ ['name' => 'admad'], ['name' => 'jadb'], ]; diff --git a/tests/Fixture/TagsFixture.php b/tests/Fixture/TagsFixture.php index 3bfddcb..9dd0952 100644 --- a/tests/Fixture/TagsFixture.php +++ b/tests/Fixture/TagsFixture.php @@ -5,22 +5,9 @@ class TagsFixture extends TestFixture { - public $table = 'slug_tags'; + public string $table = 'slug_tags'; - public $fields = [ - 'id' => ['type' => 'integer'], - 'namespace' => ['type' => 'string', 'length' => 255, 'null' => true], - 'slug' => ['type' => 'string', 'length' => 255], - 'name' => ['type' => 'string', 'length' => 320], - 'counter' => ['type' => 'integer', 'unsigned' => true, 'default' => 0, 'null' => true], - 'created' => ['type' => 'datetime', 'null' => true], - 'modified' => ['type' => 'datetime', 'null' => true], - '_constraints' => [ - 'primary' => ['type' => 'primary', 'columns' => ['id']], - ], - ]; - - public $records = [ + public array $records = [ [ 'namespace' => null, 'slug' => 'color', diff --git a/tests/TestCase/Model/Behavior/SlugBehaviorTest.php b/tests/TestCase/Model/Behavior/SlugBehaviorTest.php index 0ab1d57..c42d71e 100644 --- a/tests/TestCase/Model/Behavior/SlugBehaviorTest.php +++ b/tests/TestCase/Model/Behavior/SlugBehaviorTest.php @@ -9,7 +9,7 @@ class SlugBehaviorTest extends TestCase { - protected $fixtures = [ + protected array $fixtures = [ 'plugin.Muffin/Slug.Tags', 'plugin.Muffin/Slug.Articles', 'plugin.Muffin/Slug.ArticlesTags', @@ -212,8 +212,8 @@ public function testSlug() public function testSlugWithAssociatedTableField() { - $Articles = TableRegistry::get('Muffin/Slug.Articles', ['table' => 'slug_articles']); - $Authors = TableRegistry::get('Muffin/Slug.Authors', ['table' => 'slug_authors']); + $Articles = $this->getTableLocator()->get('Muffin/Slug.Articles', ['table' => 'slug_articles']); + $Authors = $this->getTableLocator()->get('Muffin/Slug.Authors', ['table' => 'slug_authors']); $Articles->belongsTo('Authors', ['className' => 'Muffin/Slug.Authors']); $Articles->addBehavior('Muffin/Slug.Slug', ['displayField' => ['author.name', 'title']]); @@ -229,7 +229,7 @@ public function testSlugWithAssociatedTableField() public function testBeforeSaveMultiField() { - $Articles = TableRegistry::get('Muffin/Slug.Articles', ['table' => 'slug_articles']); + $Articles = $this->getTableLocator()->get('Muffin/Slug.Articles', ['table' => 'slug_articles']); $Articles->addBehavior('Muffin/Slug.Slug', ['displayField' => ['title', 'sub_title']]); $data = ['title' => 'foo', 'sub_title' => 'bar']; @@ -268,7 +268,7 @@ public function testBeforeSaveMultiField() public function testBeforeSaveMultiWithOptionalField() { - $Articles = TableRegistry::get('Muffin/Slug.Articles', ['table' => 'slug_articles']); + $Articles = $this->getTableLocator()->get('Muffin/Slug.Articles', ['table' => 'slug_articles']); $Articles->addBehavior('Muffin/Slug.Slug', [ 'displayField' => ['title', 'sub_title'], 'implementedEvents' => [ @@ -286,8 +286,8 @@ public function testBeforeSaveMultiWithOptionalField() public function testBeforeSaveSlugGenerationWithAssociatedTableField() { - $Articles = TableRegistry::get('Muffin/Slug.Articles', ['table' => 'slug_articles']); - $Authors = TableRegistry::get('Muffin/Slug.Authors', ['table' => 'slug_authors']); + $Articles = $this->getTableLocator()->get('Muffin/Slug.Articles', ['table' => 'slug_articles']); + $Authors = $this->getTableLocator()->get('Muffin/Slug.Authors', ['table' => 'slug_authors']); $Articles->belongsTo('Authors', ['className' => 'Muffin/Slug.Authors']); $Articles->addBehavior('Muffin/Slug.Slug', ['displayField' => ['author.name', 'title']]); @@ -303,7 +303,7 @@ public function testBeforeSaveSlugGenerationWithAssociatedTableField() public function testCustomSlugField() { - $Articles = TableRegistry::get('Muffin/Slug.Articles', ['table' => 'slug_articles']); + $Articles = $this->getTableLocator()->get('Muffin/Slug.Articles', ['table' => 'slug_articles']); $Articles->addBehavior('Muffin/Slug.Slug', [ 'displayField' => 'title', 'field' => 'sub_title', @@ -415,17 +415,17 @@ public function testFinderException() $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('The `slug` key is required by the `slugged` finder'); - $result = $this->Tags->find('slugged')->first(); + $this->Tags->find('slugged')->first(); } public function testContainSluggedTables() { - TableRegistry::get('Muffin/Slug.Articles', ['table' => 'slug_articles']); + $this->getTableLocator()->get('Muffin/Slug.Articles', ['table' => 'slug_articles']); $this->Tags->belongsToMany('Muffin/Slug.Articles', [ 'foreignKey' => 'slug_tag_id', 'joinTable' => 'slug_articles_tags', - 'through' => TableRegistry::get('Muffin/Slug.ArticlesTags', ['table' => 'slug_articles_tags']), + 'through' => $this->getTableLocator()->get('Muffin/Slug.ArticlesTags', ['table' => 'slug_articles_tags']), ]); $result = $this->Tags->find('slugged', ['slug' => 'color']) diff --git a/tests/schema.php b/tests/schema.php new file mode 100644 index 0000000..151cc04 --- /dev/null +++ b/tests/schema.php @@ -0,0 +1,45 @@ + 'slug_articles', + 'columns' => [ + 'id' => ['type' => 'integer'], + 'author_id' => ['type' => 'integer'], + 'title' => ['type' => 'string', 'null' => false], + 'sub_title' => ['type' => 'string', 'null' => false], + 'slug' => ['type' => 'string', 'null' => false], + 'created' => ['type' => 'datetime', 'null' => true], + 'modified' => ['type' => 'datetime', 'null' => true], + ], + 'constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]], + ], + [ + 'table' => 'slug_articles_tags', + 'columns' => [ + 'id' => ['type' => 'integer'], + 'article_id' => ['type' => 'integer'], + 'slug_tag_id' => ['type' => 'integer'], + ], + ], + [ + 'table' => 'slug_authors', + 'columns' => [ + 'id' => ['type' => 'integer'], + 'name' => ['type' => 'string', 'null' => false], + ], + 'constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]], + ], + [ + 'table' => 'slug_tags', + 'columns' => [ + 'id' => ['type' => 'integer'], + 'namespace' => ['type' => 'string', 'length' => 255, 'null' => true], + 'slug' => ['type' => 'string', 'length' => 255], + 'name' => ['type' => 'string', 'length' => 320], + 'counter' => ['type' => 'integer', 'unsigned' => true, 'default' => 0, 'null' => true], + 'created' => ['type' => 'datetime', 'null' => true], + 'modified' => ['type' => 'datetime', 'null' => true], + ], + 'constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]], + ], +]; From 70d8cf5c092ad464cf77af51334b1178e62f72ca Mon Sep 17 00:00:00 2001 From: ADmad Date: Fri, 9 Jun 2023 22:01:04 +0530 Subject: [PATCH 2/6] Cake 5 updates --- .editorconfig | 3 +++ .gitattributes | 1 + .gitignore | 1 + composer.json | 2 +- phpstan.neon | 9 +++++++ phpunit.xml.dist | 23 +++++----------- psalm.xml | 1 + src/Model/Behavior/SlugBehavior.php | 25 +++++++----------- src/SlugPlugin.php | 2 +- src/Slugger/CakeSlugger.php | 6 ++--- src/Slugger/CocurSlugger.php | 6 ++--- .../Model/Behavior/SlugBehaviorTest.php | 26 ++++++++----------- tests/schema.php | 2 ++ 13 files changed, 52 insertions(+), 55 deletions(-) create mode 100644 phpstan.neon diff --git a/.editorconfig b/.editorconfig index bd0ddd7..406cd79 100644 --- a/.editorconfig +++ b/.editorconfig @@ -14,3 +14,6 @@ trim_trailing_whitespace = true [*.yml] indent_style = space indent_size = 2 + +[*.neon] +indent_style = tab diff --git a/.gitattributes b/.gitattributes index 1877205..e26dfb8 100644 --- a/.gitattributes +++ b/.gitattributes @@ -7,3 +7,4 @@ phpunit.xml.dist export-ignore tests export-ignore psalm.xml export-ignore +phpstan.neon export-ignore diff --git a/.gitignore b/.gitignore index 654564b..e8bd129 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /composer.lock /plugins /vendor +.phpunit.cache .phpunit.result.cache diff --git a/composer.json b/composer.json index 1a3011c..e810d4d 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,7 @@ }, "require-dev": { "cakephp/cakephp": "5.x-dev", - "phpunit/phpunit": "^9.6", + "phpunit/phpunit": "^10.1", "cocur/slugify": "^4.3" }, "autoload": { diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..34cba21 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,9 @@ +parameters: + level: 7 + checkMissingIterableValueType: false + checkGenericClassInNonGenericObjectType: false + treatPhpDocTypesAsCertain: false + bootstrapFiles: + - tests/bootstrap.php + paths: + - src diff --git a/phpunit.xml.dist b/phpunit.xml.dist index be8879d..491c4be 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,28 +1,19 @@ - - + ./tests/TestCase - - + - - - - src/ - - - + + + src/ + + diff --git a/psalm.xml b/psalm.xml index daca502..c4a31ba 100644 --- a/psalm.xml +++ b/psalm.xml @@ -7,6 +7,7 @@ xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd" findUnusedBaselineEntry="true" findUnusedCode="false" + findUnusedPsalmSuppress="true" > diff --git a/src/Model/Behavior/SlugBehavior.php b/src/Model/Behavior/SlugBehavior.php index abf0e7a..cd1f572 100644 --- a/src/Model/Behavior/SlugBehavior.php +++ b/src/Model/Behavior/SlugBehavior.php @@ -85,9 +85,9 @@ class SlugBehavior extends Behavior /** * Slugger instance. * - * @var \Muffin\Slug\SluggerInterface + * @var \Muffin\Slug\SluggerInterface|null */ - protected SluggerInterface $_slugger; + protected ?SluggerInterface $_slugger = null; /** * Constructor. @@ -136,18 +136,17 @@ public function initialize(array $config): void */ public function getSlugger(): SluggerInterface { - /** @psalm-suppress RedundantPropertyInitializationCheck */ return $this->_slugger ??= $this->_createSlugger($this->getConfig('slugger')); } /** * Set slugger instance. * - * @param \Muffin\Slug\SluggerInterface|class-string<\Muffin\Slug\SluggerInterface>|array $slugger Sets slugger instance. + * @param \Muffin\Slug\SluggerInterface|array|class-string<\Muffin\Slug\SluggerInterface> $slugger Sets slugger instance. * Can be SluggerInterface instance or class name or config array. * @return void */ - public function setSlugger(SluggerInterface|string|array $slugger): void + public function setSlugger(SluggerInterface|array|string $slugger): void { $this->_slugger = $this->_createSlugger($slugger); } @@ -155,12 +154,11 @@ public function setSlugger(SluggerInterface|string|array $slugger): void /** * Create slugger instance * - * @param \Muffin\Slug\SluggerInterface|class-string<\Muffin\Slug\SluggerInterface>|array $slugger Sets slugger instance. + * @param \Muffin\Slug\SluggerInterface|array|class-string<\Muffin\Slug\SluggerInterface> $slugger Sets slugger instance. * Can be SluggerInterface instance or class name or config array. * @return \Muffin\Slug\SluggerInterface - * @psalm-suppress MoreSpecificReturnType */ - protected function _createSlugger(SluggerInterface|string|array $slugger): SluggerInterface + protected function _createSlugger(SluggerInterface|array|string $slugger): SluggerInterface { if (is_string($slugger)) { return new $slugger(); @@ -272,17 +270,13 @@ protected function _getPartsFromEntity(EntityInterface $entity): array * Custom finder. * * @param \Cake\ORM\Query\SelectQuery $query Query. - * @param array $options Options. + * @param string $slug Slug to search for. * @return \Cake\ORM\Query\SelectQuery Query. */ - public function findSlugged(SelectQuery $query, array $options): SelectQuery + public function findSlugged(SelectQuery $query, string $slug): SelectQuery { - if (!isset($options['slug'])) { - throw new InvalidArgumentException('The `slug` key is required by the `slugged` finder.'); - } - return $query->where([ - $this->_table->aliasField($this->getConfig('field')) => $options['slug'], + $this->_table->aliasField($this->getConfig('field')) => $slug, ]); } @@ -411,7 +405,6 @@ protected function _slug(string $string, string $separator): string { $replacements = $this->getConfig('replacements'); $slugger = $this->getSlugger(); - /** @psalm-suppress PossiblyNullReference */ $slug = $slugger->slug(str_replace(array_keys($replacements), $replacements, $string), $separator); if ($this->getConfig('maxLength')) { $slug = Text::truncate( diff --git a/src/SlugPlugin.php b/src/SlugPlugin.php index be21e54..dfb458a 100644 --- a/src/SlugPlugin.php +++ b/src/SlugPlugin.php @@ -5,7 +5,7 @@ use Cake\Core\BasePlugin; -class Plugin extends BasePlugin +class SlugPlugin extends BasePlugin { /** * The name of this plugin diff --git a/src/Slugger/CakeSlugger.php b/src/Slugger/CakeSlugger.php index c89e936..3d0d0bc 100644 --- a/src/Slugger/CakeSlugger.php +++ b/src/Slugger/CakeSlugger.php @@ -18,16 +18,16 @@ class CakeSlugger implements SluggerInterface * - `lowercase` - Boolean indication whether slug should be lowercased. * Defaults to true. * - * @var array + * @var array */ - protected $config = [ + protected array $config = [ 'lowercase' => true, ]; /** * Constructor * - * @param array $config Configuration. + * @param array $config Configuration. */ public function __construct(array $config = []) { diff --git a/src/Slugger/CocurSlugger.php b/src/Slugger/CocurSlugger.php index 037b95f..9b8b94f 100644 --- a/src/Slugger/CocurSlugger.php +++ b/src/Slugger/CocurSlugger.php @@ -18,9 +18,9 @@ class CocurSlugger implements SluggerInterface * - `lowercase` - Boolean indication whether slug should be lowercased. * Default to true. * - * @var array + * @var array */ - protected $config = [ + protected array $config = [ 'regex' => null, 'lowercase' => true, ]; @@ -28,7 +28,7 @@ class CocurSlugger implements SluggerInterface /** * Constructor * - * @param array $config Configuration. + * @param array $config Configuration. */ public function __construct(array $config = []) { diff --git a/tests/TestCase/Model/Behavior/SlugBehaviorTest.php b/tests/TestCase/Model/Behavior/SlugBehaviorTest.php index c42d71e..7f38556 100644 --- a/tests/TestCase/Model/Behavior/SlugBehaviorTest.php +++ b/tests/TestCase/Model/Behavior/SlugBehaviorTest.php @@ -4,8 +4,10 @@ namespace Muffin\Slug\Test\TestCase\Model\Behavior; use Cake\ORM\Entity; -use Cake\ORM\TableRegistry; +use Cake\ORM\Table; use Cake\TestSuite\TestCase; +use InvalidArgumentException; +use Muffin\Slug\Model\Behavior\SlugBehavior; class SlugBehaviorTest extends TestCase { @@ -16,6 +18,9 @@ class SlugBehaviorTest extends TestCase 'plugin.Muffin/Slug.Authors', ]; + protected Table $Tags; + protected SlugBehavior $Behavior; + public function setUp(): void { parent::setUp(); @@ -360,9 +365,8 @@ public function testCustomMaxLength() public function testSlugThrowsInvalidArgumentException() { - $this->expectException(\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); - $tag = $this->Tags->newEmptyEntity(); $this->Behavior->slug($this->Tags->newEntity([])); } @@ -375,7 +379,7 @@ public function testSlugUnchanged() $expected = 'my-slug'; $this->assertEquals($expected, $result); - $tag = $this->Tags->find('slugged', ['slug' => 'dark-color'])->first(); + $tag = $this->Tags->find('slugged', slug: 'dark-color')->first(); $tag = $this->Tags->patchEntity($tag, ['name' => 'new name']); $result = $this->Tags->save($tag)->slug; $expected = 'dark-color'; @@ -395,7 +399,7 @@ public function testSavingEntityWithErrors() public function testFinder() { - $result = $this->Tags->find('slugged', ['slug' => 'dark-color']) + $result = $this->Tags->find('slugged', slug: 'dark-color') ->select(['slug', 'name']) ->first() ->toArray(); @@ -406,18 +410,10 @@ public function testFinder() ]; $this->assertEquals($expected, $result); - $query = $this->Tags->find('slugged', ['slug' => 0]); + $query = $this->Tags->find('slugged', slug: '0'); $this->assertInstanceOf('Cake\ORM\Query', $query); } - public function testFinderException() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The `slug` key is required by the `slugged` finder'); - - $this->Tags->find('slugged')->first(); - } - public function testContainSluggedTables() { $this->getTableLocator()->get('Muffin/Slug.Articles', ['table' => 'slug_articles']); @@ -428,7 +424,7 @@ public function testContainSluggedTables() 'through' => $this->getTableLocator()->get('Muffin/Slug.ArticlesTags', ['table' => 'slug_articles_tags']), ]); - $result = $this->Tags->find('slugged', ['slug' => 'color']) + $result = $this->Tags->find('slugged', slug: 'color') ->contain(['Articles']) ->first() ->toArray(); diff --git a/tests/schema.php b/tests/schema.php index 151cc04..257ce9f 100644 --- a/tests/schema.php +++ b/tests/schema.php @@ -1,4 +1,6 @@ 'slug_articles', From 568fbdabd0cb63a729d287c20888132b85ca211a Mon Sep 17 00:00:00 2001 From: ADmad Date: Fri, 9 Jun 2023 22:17:15 +0530 Subject: [PATCH 3/6] Update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6298669..c04d118 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ If you want to find a record using its slug, a custom finder is provided by the ```php // src/Controller/ExamplesController.php -$example = $this->Examples->find('slugged', ['slug' => $slug]); +$example = $this->Examples->find('slugged', slug: $slug); ``` ## Configuration From b751e1b4eb5f2c581d3810ec93ab3907ffe57f9d Mon Sep 17 00:00:00 2001 From: ADmad Date: Wed, 27 Sep 2023 08:59:47 +0530 Subject: [PATCH 4/6] Update to Cake 5 stable --- composer.json | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/composer.json b/composer.json index e810d4d..6e75886 100644 --- a/composer.json +++ b/composer.json @@ -31,10 +31,10 @@ "source": "https://github.com/usemuffin/slug" }, "require": { - "cakephp/orm": "5.x-dev" + "cakephp/orm": "^5.0" }, "require-dev": { - "cakephp/cakephp": "5.x-dev", + "cakephp/cakephp": "5.0", "phpunit/phpunit": "^10.1", "cocur/slugify": "^4.3" }, @@ -53,7 +53,5 @@ "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true } - }, - "minimum-stability": "dev", - "prefer-stable": true + } } From 98cd8d9386d0a897f50614dda0dc560e32db24d2 Mon Sep 17 00:00:00 2001 From: ADmad Date: Wed, 27 Sep 2023 09:02:12 +0530 Subject: [PATCH 5/6] update .gitattributes --- .gitattributes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index e26dfb8..948170f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,7 +3,7 @@ .editorconfig export-ignore .gitattributes export-ignore .gitignore export-ignore -.travis.yml export-ignore +.github export-ignore phpunit.xml.dist export-ignore tests export-ignore psalm.xml export-ignore From 0fd615d0a9495b4ef8767ad684ee7948b955d571 Mon Sep 17 00:00:00 2001 From: ADmad Date: Wed, 27 Sep 2023 09:09:02 +0530 Subject: [PATCH 6/6] Fix badge --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c04d118..8a915a8 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Slug -[![Build Status](https://img.shields.io/github/workflow/status/UseMuffin/Slug/CI/master)](https://github.com/UseMuffin/Slug/actions) +[![Build Status](https://img.shields.io/github/actions/workflow/status/UseMuffin/Slug/ci.yml?style=flat-square +)](https://github.com/UseMuffin/Slug/actions) [![Coverage](https://img.shields.io/codecov/c/github/UseMuffin/Slug/master.svg?style=flat-square)](https://codecov.io/github/UseMuffin/Slug) [![Total Downloads](https://img.shields.io/packagist/dt/muffin/slug.svg?style=flat-square)](https://packagist.org/packages/muffin/slug) [![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](LICENSE)