diff --git a/.editorconfig b/.editorconfig index a84f86c..41e9eef 100644 --- a/.editorconfig +++ b/.editorconfig @@ -15,4 +15,7 @@ end_of_line = crlf [*.yml] indent_style = space -indent_size = 2 \ No newline at end of file +indent_size = 2 + +[*.neon] +indent_style = tab diff --git a/.gitattributes b/.gitattributes index 17493ec..931f1a3 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,7 +2,9 @@ .editorconfig export-ignore .gitattributes export-ignore .gitignore export-ignore -.semver export-ignore +.github export-ignore phpunit.xml.dist export-ignore -.travis.yml export-ignore tests export-ignore +psalm.xml export-ignore +psalm-baseline.xml export-ignore +phpstan.neon export-ignore diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..1b40453 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,21 @@ +name: CI + +on: + push: + branches: + - master + pull_request: + branches: + - '*' + +permissions: + contents: read + +jobs: + testsuite: + uses: ADmad/.github/.github/workflows/testsuite-with-db.yml@master + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + + cs-stan: + uses: ADmad/.github/.github/workflows/cs-stan.yml@master diff --git a/.gitignore b/.gitignore index 364f9bd..e8bd129 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ /composer.lock /plugins /vendor +.phpunit.cache +.phpunit.result.cache diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 33a5876..0000000 --- a/.travis.yml +++ /dev/null @@ -1,56 +0,0 @@ -language: php - -php: - - 5.6 - - 7.4 - -services: - - mysql - - postgresql - -env: - matrix: - - DB=mysql db_dsn='mysql://root@127.0.0.1/cakephp_test' - - DB=pgsql db_dsn='postgres://postgres@127.0.0.1/cakephp_test' - - DB=sqlite db_dsn='sqlite:///:memory:' - - global: - - DEFAULT=1 - -matrix: - fast_finish: true - - include: - - php: 7.2 - env: PHPCS=1 DEFAULT=0 - - - php: 7.2 - env: PHPSTAN=1 DEFAULT=0 - - - php: 5.6 - env: PREFER_LOWEST=1 - -before_script: - - if [[ $TRAVIS_PHP_VERSION != 7.4 ]]; then phpenv config-rm xdebug.ini; fi - - - if [[ $PREFER_LOWEST != 1 ]]; then composer install --no-interaction; fi - - if [[ $PREFER_LOWEST = 1 ]]; then composer update --no-interaction --prefer-lowest --prefer-stable; fi - - - if [[ $DB = 'mysql' ]]; then mysql -e 'CREATE DATABASE cakephp_test;'; fi - - if [[ $DB = 'pgsql' ]]; then psql -c 'CREATE DATABASE cakephp_test;' -U postgres; fi - - - if [[ $PHPCS = 1 ]]; then composer require cakephp/cakephp-codesniffer:"^3.0"; fi - -script: - - if [[ $DEFAULT = 1 && $TRAVIS_PHP_VERSION != 7.4 ]]; then vendor/bin/phpunit; fi - - if [[ $DEFAULT = 1 && $TRAVIS_PHP_VERSION = 7.4 ]]; then vendor/bin/phpunit --coverage-clover=clover.xml; fi - - - if [[ $PHPCS = 1 ]]; then vendor/bin/phpcs -n -p --extensions=php --standard=vendor/cakephp/cakephp-codesniffer/CakePHP ./src ./tests; fi - - - if [[ $PHPSTAN = 1 ]]; then composer require --dev phpstan/phpstan:^0.12 && vendor/bin/phpstan analyse -l 5 src; fi - -after_success: - - if [[ $DEFAULT = 1 && $TRAVIS_PHP_VERSION = 7.4 ]]; then bash <(curl -s https://codecov.io/bash); fi - -notifications: - email: false diff --git a/LICENSE.txt b/LICENSE.txt index 5137da5..d7be439 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2015 Use Muffin +Copyright (c) 2015-Present Use Muffin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 4ed6593..e2bdef6 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # Orderly -[![Build Status](https://img.shields.io/travis/UseMuffin/Orderly/master.svg?style=flat-square)](https://travis-ci.org/UseMuffin/Orderly) -[![Coverage](https://img.shields.io/codecov/c/github/UseMuffin/Orderly.svg?style=flat-square)](https://codecov.io/github/UseMuffin/Orderly) +[![Build Status](https://img.shields.io/github/actions/workflow/status/UseMuffin/Orderly/ci.yml?branch=master&style=flat-square)](https://github.com/UseMuffin/Orderly/actions/workflows/ci.yml?query=branch%3Amaster) +[![Coverage](https://img.shields.io/codecov/c/github/UseMuffin/Orderly/master?style=flat-square +)](https://app.codecov.io/gh/UseMuffin/Orderly) [![Total Downloads](https://img.shields.io/packagist/dt/muffin/orderly.svg?style=flat-square)](https://packagist.org/packages/muffin/orderly) [![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](LICENSE) @@ -11,20 +12,19 @@ Allows setting default order for your tables. Using [Composer][composer]: -``` +```sh composer require muffin/orderly ``` Then load the plugin using the console command: -``` +```sh bin/cake plugin load Muffin/Orderly ``` ## What is does -Orderly allow you to set default `ORDER` clause for your table queries, similar -to the functionality provided by `Model::$order` property of CakePHP 2.x. +Orderly allow you to set default `ORDER` clause for your table's `SELECT` queries. ## Usage @@ -38,7 +38,7 @@ $this->addBehavior('Muffin/Orderly.Orderly'); $this->addBehavior('Muffin/Orderly.Orderly', ['order' => $this->aliasField('field_name')]); ``` -Value for `order` key can any valid value that `\Cake\ORM\Query::order()` takes. +Value for `order` key can any valid value that `\Cake\ORM\Query\SelectQuery::orderBy()` takes. The default order clause will only be applied to the primary query and when no custom order clause has already been set for the query. @@ -48,7 +48,7 @@ condition using `callback` option. The order will be applied if callback returns ```php $this->addBehavior('Muffin/Orderly.Orderly', [ 'order' => ['Alias.field_name' => 'DESC'], - 'callback' => function (\Cake\ORM\Query $query, \ArrayObject $options, $primary) { + 'callback' => function (SelectQuery $query, ArrayObject $options, bool $primary) { //return a boolean } ]); @@ -61,13 +61,13 @@ on return value of their respective callbacks: $this->addBehavior('Muffin/Orderly.Orderly', [ [ 'order' => ['Alias.field_name' => 'DESC'], - 'callback' => function (\Cake\ORM\Query $query, \ArrayObject $options, $primary) { + 'callback' => function (SelectQuery $query, ArrayObject $options, bool $primary) { //return a boolean } ], [ 'order' => ['Alias.another_field'], - 'callback' => function (\Cake\ORM\Query $query, \ArrayObject $options, $primary) { + 'callback' => function (SelectQuery $query, ArrayObject $options, bool $primary) { //return a boolean } ], @@ -97,4 +97,4 @@ Copyright (c) 2015-Present, [Use Muffin][muffin] and licensed under [The MIT Lic [composer]:http://getcomposer.org [mit]:http://www.opensource.org/licenses/mit-license.php [muffin]:http://usemuffin.com -[standards]:http://book.cakephp.org/3/en/contributing/cakephp-coding-conventions.html +[standards]:http://book.cakephp.org/5/en/contributing/cakephp-coding-conventions.html diff --git a/composer.json b/composer.json index 3f49c56..f1743d9 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "muffin/orderly", - "description": "Default ordering (behavior) for CakePHP 3", + "description": "Default ordering (behavior) for CakePHP", "type": "cakephp-plugin", "keywords": [ "cakephp", @@ -30,12 +30,11 @@ "source": "https://github.com/usemuffin/orderly" }, "require": { - "cakephp/orm": "^3.4" + "cakephp/orm": "^5.0" }, "require-dev": { - "cakephp/cakephp": "^3.4", - "cakephp/chronos": "^1.1", - "phpunit/phpunit": "^5.7.14|^6.0" + "cakephp/cakephp": "^5.0", + "phpunit/phpunit": "^10.5.58 || ^11.5.3 || ^12.4" }, "autoload": { "psr-4": { @@ -47,5 +46,11 @@ "Muffin\\Orderly\\Test\\": "tests", "Cake\\Test\\Fixture\\": "vendor/cakephp/cakephp/tests/Fixture" } + }, + "config": { + "sort-packages": true, + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true + } } } diff --git a/phpcs.xml b/phpcs.xml new file mode 100644 index 0000000..e39be6f --- /dev/null +++ b/phpcs.xml @@ -0,0 +1,7 @@ + + + + + src/ + tests/ + \ No newline at end of file diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..4ee867a --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,9 @@ +parameters: + level: 7 + treatPhpDocTypesAsCertain: false + bootstrapFiles: + - tests/bootstrap.php + paths: + - src + ignoreErrors: + - identifier: missingType.generics diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 00aa220..288487c 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,30 +1,19 @@ - - + ./tests/ - - - - - - - - - - - - + + + + + + + + ./src/ - - + + diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 0000000..442fe0f --- /dev/null +++ b/psalm.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + diff --git a/src/Model/Behavior/OrderlyBehavior.php b/src/Model/Behavior/OrderlyBehavior.php index 9ad71a5..cdff8e7 100644 --- a/src/Model/Behavior/OrderlyBehavior.php +++ b/src/Model/Behavior/OrderlyBehavior.php @@ -1,20 +1,22 @@ $config Config * @return void */ - public function initialize(array $config) + public function initialize(array $config): void { parent::initialize($config); @@ -22,15 +24,15 @@ public function initialize(array $config) } /** - * Add default order clause to query as necessary. + * Add the default order clause to the query as necessary. * - * @param \Cake\Event\Event $event Event - * @param \Cake\ORM\Query $query Query + * @param \Cake\Event\EventInterface $event Event + * @param \Cake\ORM\Query\SelectQuery $query Query * @param \ArrayObject $options Options * @param bool $primary Boolean indicating whether it's primary query. * @return void */ - public function beforeFind(Event $event, Query $query, ArrayObject $options, $primary) + public function beforeFind(EventInterface $event, SelectQuery $query, ArrayObject $options, bool $primary): void { if ($query->clause('order')) { return; @@ -42,7 +44,7 @@ public function beforeFind(Event $event, Query $query, ArrayObject $options, $pr empty($config['callback']) || call_user_func($config['callback'], $query, $options, $primary) ) { - $query->order($config['order']); + $query->orderBy($config['order']); } } } @@ -50,19 +52,24 @@ public function beforeFind(Event $event, Query $query, ArrayObject $options, $pr /** * Normalize configuration. * - * @param mixed $orders Orders config + * @param array $orders Orders config * @return void */ - protected function _normalizeConfig($orders) + protected function _normalizeConfig(array $orders): void { - if (empty($orders)) { + unset($orders['className']); + + if ($orders === []) { $orders = [[]]; } elseif (isset($orders['order']) || isset($orders['callback'])) { $orders = [$orders]; } $default = [ - 'order' => $this->_table->aliasField($this->_table->getDisplayField()), + 'order' => array_map( + $this->_table->aliasField(...), + (array)$this->_table->getDisplayField(), + ), 'callback' => null, ]; diff --git a/src/OrderlyPlugin.php b/src/OrderlyPlugin.php new file mode 100644 index 0000000..3d4f572 --- /dev/null +++ b/src/OrderlyPlugin.php @@ -0,0 +1,37 @@ +Table = TableRegistry::get('Posts'); + $this->Table = $this->getTableLocator()->get('Posts'); } - public function tearDown() + public function tearDown(): void { parent::tearDown(); - TableRegistry::clear(); - unset($this->Table, $this->Behavior); + $this->getTableLocator()->clear(); + unset($this->Table); } public function testInitialize() @@ -33,13 +38,13 @@ public function testInitialize() $expected = [ [ - 'order' => $this->Table->aliasField($this->Table->getDisplayField()), + 'order' => ['Posts.title'], 'callback' => null, ], ]; $this->assertEquals( $expected, - $this->Table->behaviors()->Orderly->getConfig()['orders'] + $this->Table->behaviors()->Orderly->getConfig()['orders'], ); $this->Table->removeBehavior('Orderly'); @@ -53,7 +58,7 @@ public function testInitialize() ]; $this->assertEquals( $expected, - $this->Table->behaviors()->Orderly->getConfig()['orders'] + $this->Table->behaviors()->Orderly->getConfig()['orders'], ); $callback = function () { @@ -64,13 +69,13 @@ public function testInitialize() $expected = [ [ - 'order' => 'Posts.title', + 'order' => ['Posts.title'], 'callback' => $callback, ], ]; $this->assertEquals( $expected, - $this->Table->behaviors()->Orderly->getConfig()['orders'] + $this->Table->behaviors()->Orderly->getConfig()['orders'], ); $this->Table->removeBehavior('Orderly'); @@ -81,7 +86,7 @@ public function testInitialize() $expected = [ [ - 'order' => 'Posts.title', + 'order' => ['Posts.title'], 'callback' => null, ], [ @@ -91,7 +96,7 @@ public function testInitialize() ]; $this->assertEquals( $expected, - $this->Table->behaviors()->Orderly->getConfig()['orders'] + $this->Table->behaviors()->Orderly->getConfig()['orders'], ); } @@ -102,7 +107,7 @@ public function testBeforeFind() $event = new Event('Model.beforeFind', $this); $query = $this->Table->query(); - $behavior->beforeFind($event, $query, new \ArrayObject(), true); + $behavior->beforeFind($event, $query, new ArrayObject(), true); $this->assertEquals(1, count($query->clause('order'))); } @@ -113,8 +118,8 @@ public function testBeforeFindQueryWithOrder() $event = new Event('Model.beforeFind', $this); $query = $this->Table->find() - ->order('author_id'); - $behavior->beforeFind($event, $query, new \ArrayObject(), true); + ->orderBy('author_id'); + $behavior->beforeFind($event, $query, new ArrayObject(), true); $this->assertEquals(1, count($query->clause('order'))); } @@ -147,24 +152,24 @@ public function testCallback() $event = new Event('Model.beforeFind', $this); $query = $this->Table->find(); - $behavior->beforeFind($event, $query, new \ArrayObject(['field' => null]), true); + $behavior->beforeFind($event, $query, new ArrayObject(['field' => null]), true); $this->assertNull($query->clause('order')); $valueBinder = new ValueBinder(); - $behavior->beforeFind($event, $query, new \ArrayObject(['field' => 'first']), true); + $behavior->beforeFind($event, $query, new ArrayObject(['field' => 'first']), true); $orderClause = $query->clause('order'); $this->assertCount(1, $orderClause); $this->assertEquals('ORDER BY first', $orderClause->sql($valueBinder)); $query = $this->Table->find(); - $behavior->beforeFind($event, $query, new \ArrayObject(['field' => 'second']), true); + $behavior->beforeFind($event, $query, new ArrayObject(['field' => 'second']), true); $orderClause = $query->clause('order'); $this->assertCount(1, $orderClause); $this->assertEquals('ORDER BY second', $orderClause->sql($valueBinder)); $query = $this->Table->find(); - $behavior->beforeFind($event, $query, new \ArrayObject(['field' => '_all_']), true); + $behavior->beforeFind($event, $query, new ArrayObject(['field' => '_all_']), true); $orderClause = $query->clause('order'); $this->assertCount(2, $orderClause); $this->assertEquals('ORDER BY first, second', $orderClause->sql($valueBinder)); diff --git a/tests/bootstrap.php b/tests/bootstrap.php index c6dc570..a66fd87 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,4 +1,6 @@ 'posts', + 'columns' => [ + 'id' => [ + 'type' => 'integer', + ], + 'author_id' => [ + 'type' => 'integer', + 'null' => false, + ], + 'title' => [ + 'type' => 'string', + 'null' => false, + ], + 'body' => 'text', + 'published' => [ + 'type' => 'string', + 'length' => 1, + 'default' => 'N', + ], + ], + 'constraints' => [ + 'primary' => [ + 'type' => 'primary', + 'columns' => [ + 'id', + ], + ], + ], + ], +];