diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 22a95cfe..a8062907 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,11 +13,11 @@ jobs: strategy: fail-fast: false matrix: - php-version: ['7.4', '8.2'] + php-version: ['8.1', '8.2'] db-type: [sqlite, mysql, pgsql] prefer-lowest: [''] include: - - php-version: '7.4' + - php-version: '8.1' db-type: 'sqlite' prefer-lowest: 'prefer-lowest' @@ -69,7 +69,7 @@ jobs: if [[ ${{ matrix.db-type }} == 'sqlite' ]]; then export DB_URL='sqlite:///:memory:'; fi if [[ ${{ matrix.db-type }} == 'mysql' ]]; then export DB_URL='mysql://root:root@127.0.0.1/cakephp'; fi if [[ ${{ matrix.db-type }} == 'pgsql' ]]; then export DB_URL='postgres://postgres:postgres@127.0.0.1/postgres'; fi - if [[ ${{ matrix.php-version }} == '7.4' && ${{ matrix.db-type }} == 'sqlite' ]] + if [[ ${{ matrix.php-version }} == '8.1' && ${{ matrix.db-type }} == 'sqlite' ]] then vendor/bin/phpunit --coverage-clover=coverage.xml else @@ -79,7 +79,7 @@ jobs: run: if ${{ matrix.prefer-lowest == 'prefer-lowest' }}; then vendor/bin/validate-prefer-lowest -m; fi - name: Code Coverage Report - if: success() && matrix.php-version == '7.4' && matrix.db-type == 'sqlite' + if: success() && matrix.php-version == '8.1' && matrix.db-type == 'sqlite' uses: codecov/codecov-action@v3 validation: @@ -92,7 +92,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: '7.4' + php-version: '8.1' extensions: mbstring, intl coverage: none diff --git a/.gitignore b/.gitignore index 8681f007..9ecd8b69 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ phpunit.phar -.phpunit.result.cache +.phpunit.cache composer.lock vendor/ plugins/ diff --git a/README.md b/README.md index 44888ae2..2d32407d 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,14 @@ [![CI](https://github.com/dereuromark/cakephp-ajax/workflows/CI/badge.svg?branch=master)](https://github.com/dereuromark/cakephp-ajax/actions?query=workflow%3ACI+branch%3Amaster) [![Coverage Status](https://codecov.io/gh/dereuromark/cakephp-ajax/branch/master/graph/badge.svg)](https://codecov.io/gh/dereuromark/cakephp-ajax) [![Latest Stable Version](https://poser.pugx.org/dereuromark/cakephp-ajax/v/stable.svg)](https://packagist.org/packages/dereuromark/cakephp-ajax) -[![Minimum PHP Version](https://img.shields.io/badge/php-%3E%3D%207.4-8892BF.svg)](https://php.net/) +[![Minimum PHP Version](https://img.shields.io/badge/php-%3E%3D%208.1-8892BF.svg)](https://php.net/) [![License](https://poser.pugx.org/dereuromark/cakephp-ajax/license.svg)](https://packagist.org/packages/dereuromark/cakephp-ajax) [![Total Downloads](https://poser.pugx.org/dereuromark/cakephp-ajax/d/total.svg)](https://packagist.org/packages/dereuromark/cakephp-ajax) [![Coding Standards](https://img.shields.io/badge/cs-PSR--2--R-yellow.svg)](https://github.com/php-fig-rectified/fig-rectified-standards) A CakePHP plugin that makes working with AJAX a piece of cake.. -This branch is for **CakePHP 4.2+**. For details see [version map](https://github.com/dereuromark/cakephp-ajax/wiki#cakephp-version-map). +This branch is for **CakePHP 5.0+**. For details see [version map](https://github.com/dereuromark/cakephp-ajax/wiki#cakephp-version-map). ## What is this plugin for? Basically DRY (Don't repeat yourself) and easy AJAX handling. diff --git a/composer.json b/composer.json index 8399b1e6..848c2fe3 100644 --- a/composer.json +++ b/composer.json @@ -19,14 +19,15 @@ } ], "require": { - "php": ">=7.4", - "cakephp/cakephp": "^4.2.0" + "php": ">=8.1", + "cakephp/cakephp": "^5.0.0" }, "require-dev": { - "dereuromark/cakephp-tools": "^2.0.0", - "phpunit/phpunit": "^9.5", - "fig-r/psr2r-sniffer": "dev-master" - }, + "dereuromark/cakephp-tools": "^3.0.0", + "phpunit/phpunit": "^10.2.1", + "fig-r/psr2r-sniffer": "dev-next", + "ext-mbstring": "*" + }, "autoload": { "psr-4": { "Ajax\\": "src/" @@ -44,6 +45,7 @@ "issues": "https://github.com/dereuromark/cakephp-ajax/issues" }, "prefer-stable": true, + "minimum-stability": "dev", "scripts": { "test": "vendor/bin/phpunit", "test-coverage": "vendor/bin/phpunit --log-junit tmp/coverage/unitreport.xml --coverage-html tmp/coverage --coverage-clover tmp/coverage/coverage.xml", diff --git a/docs/Component/Ajax.md b/docs/Component/Ajax.md index 13a12761..5a247c32 100644 --- a/docs/Component/Ajax.md +++ b/docs/Component/Ajax.md @@ -9,12 +9,6 @@ follow those redirects and return the content via requestAction(). This might no This plugin prevents this internal request, and instead returns the URL and status code inside the JSON response. -### Disable internal requests -Make sure you disabled the deprecated `enableBeforeRedirect` option: -```php -$this->loadComponent('RequestHandler', ['enableBeforeRedirect' => false]); -``` - ## Setup Load the Ajax component inside `Controller::initialize()`: ```php @@ -63,12 +57,12 @@ are not reserved: ```php $content = ['id' => 1, 'title' => 'title']; $this->set(compact('content')); -$this->set('_serialize', ['content']); +$this->set('serialize', ['content']); ``` results in "content":{...}, ... - + ### AJAX Delete For usability reasons you might want to delete a row in a paginated table, without the need to refresh the whole page. @@ -84,7 +78,7 @@ public function delete($id = null) { $this->request->allowMethod(['post', 'delete']); $group = $this->Groups->get($id); - $this->Groups->delete($group); + $this->Groups->deleteOrFail($group); $this->Flash->success(__('The group has been deleted.')); return $this->redirect(['action' => 'index']); @@ -116,7 +110,7 @@ public function delete($id = null) { return $this->redirect($this->referer(['action' => 'index'], true)); } - $this->Groups->delete($group); + $this->Groups->deleteOrFail($group); $this->Flash->success(__('The group has been deleted.')); return $this->redirect(['action' => 'index']); diff --git a/docs/Contributing.md b/docs/Contributing.md index 3a9dfa9c..d8f9e3e8 100644 --- a/docs/Contributing.md +++ b/docs/Contributing.md @@ -17,7 +17,6 @@ There are a few guidelines that I need contributors to follow: Tip: You can use the composer commands to set up everything: * `composer install` -* `composer test-setup` Now you can run the tests via `composer test` and get coverage via `composer test-coverage` commands. diff --git a/docs/README.md b/docs/README.md index 9b52ec4f..1705cda3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -3,9 +3,6 @@ ## Installation * [Installation](Install.md) -## Upgrade Guide -* [Upgrade guide from 2.x to 3.x](Upgrade.md) - ## Documentation * [Ajax plugin](Ajax.md) * [View/Ajax](View/Ajax.md) diff --git a/docs/View/Ajax.md b/docs/View/Ajax.md index 063e9586..5a6c0267 100644 --- a/docs/View/Ajax.md +++ b/docs/View/Ajax.md @@ -42,7 +42,7 @@ The result can be this, for example: "error": '' } ``` -You can add more data to the response object via `_serialize`. +You can add more data to the response object via `serialize`. ### Drop down selections diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 975d97a5..9ff371ec 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,26 +1,26 @@ - + + - - + + - tests/ + tests/TestCase/ - - - - - - - - + + + + src/ - + diff --git a/src/Controller/Component/AjaxComponent.php b/src/Controller/Component/AjaxComponent.php index 023d8033..86144e98 100644 --- a/src/Controller/Component/AjaxComponent.php +++ b/src/Controller/Component/AjaxComponent.php @@ -26,12 +26,12 @@ class AjaxComponent extends Component { /** * @var bool */ - protected $respondAsAjax = false; + public bool $respondAsAjax = false; /** * @var array */ - protected $_defaultConfig = [ + protected array $_defaultConfig = [ 'viewClass' => 'Ajax.Ajax', 'autoDetect' => true, 'resolveRedirect' => true, @@ -78,7 +78,7 @@ public function beforeRender(EventInterface $event): void { /** * @return void */ - protected function _respondAsAjax() { + protected function _respondAsAjax(): void { $this->getController()->viewBuilder()->setClassName($this->_config['viewClass']); // Set flash messages to the view @@ -87,16 +87,16 @@ protected function _respondAsAjax() { $this->getController()->set('_message', $message); } - // If _serialize is true, *all* viewVars will be serialized; no need to add _message. + // If `serialize` is true, *all* viewVars will be serialized; no need to add _message. if ($this->_isControllerSerializeTrue()) { return; } $serializeKeys = ['_message']; - if (!empty($this->getController()->viewBuilder()->getVar('_serialize'))) { - $serializeKeys = array_merge($serializeKeys, (array)$this->getController()->viewBuilder()->getVar('_serialize')); + if (!empty($this->getController()->viewBuilder()->getVar('serialize'))) { + $serializeKeys = array_merge($serializeKeys, (array)$this->getController()->viewBuilder()->getVar('serialize')); } - $this->getController()->set('_serialize', $serializeKeys); + $this->getController()->set('serialize', $serializeKeys); } /** @@ -129,10 +129,10 @@ public function beforeRedirect(EventInterface $event, $url, Response $response): } $serializeKeys = ['_redirect']; - if ($this->getController()->viewBuilder()->getVar('_serialize')) { - $serializeKeys = array_merge($serializeKeys, (array)$this->getController()->viewBuilder()->getVar('_serialize')); + if ($this->getController()->viewBuilder()->getVar('serialize')) { + $serializeKeys = array_merge($serializeKeys, (array)$this->getController()->viewBuilder()->getVar('serialize')); } - $this->getController()->set('_serialize', $serializeKeys); + $this->getController()->set('serialize', $serializeKeys); // Further changes will be required here when the change to immutable response objects is completed $response = $this->getController()->render(); @@ -140,12 +140,12 @@ public function beforeRedirect(EventInterface $event, $url, Response $response): } /** - * Checks to see if the Controller->viewVar labeled _serialize is set to boolean true. + * Checks to see if the Controller->viewVar labeled `serialize` is set to boolean true. * * @return bool */ - protected function _isControllerSerializeTrue() { - if ($this->getController()->viewBuilder()->getVar('_serialize') === true) { + protected function _isControllerSerializeTrue(): bool { + if ($this->getController()->viewBuilder()->getVar('serialize') === true) { return true; } @@ -157,7 +157,7 @@ protected function _isControllerSerializeTrue() { * * @return bool */ - protected function _isActionEnabled() { + protected function _isActionEnabled(): bool { $actions = $this->getConfig('actions'); if (!$actions) { return true; diff --git a/src/Plugin.php b/src/Plugin.php index 5ce69bf8..40b5129a 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -9,16 +9,16 @@ class Plugin extends BasePlugin { /** * @var bool */ - protected $middlewareEnabled = false; + protected bool $middlewareEnabled = false; /** * @var bool */ - protected $consoleEnabled = false; + protected bool $consoleEnabled = false; /** * @var bool */ - protected $routesEnabled = false; + protected bool $routesEnabled = false; } diff --git a/src/View/AjaxView.php b/src/View/AjaxView.php index 8b1c737f..6e728b1b 100644 --- a/src/View/AjaxView.php +++ b/src/View/AjaxView.php @@ -16,7 +16,7 @@ * A response to an invalid request may be just HTTP status "code" and error "message" * (e.g, on 4xx or 5xx). * A response to a valid request will always contain at least "content" and "error" keys. - * You can add more data using _serialize. + * You can add more data using `serialize`. * * @author Mark Scherer * @license http://opensource.org/licenses/mit-license.php MIT @@ -30,7 +30,7 @@ class AjaxView extends AppView { * * @var array */ - protected $_passedVars = [ + protected array $_passedVars = [ 'viewVars', 'autoLayout', 'ext', 'helpers', 'view', 'layout', 'name', 'theme', 'layoutPath', 'plugin', 'passedArgs', 'subDir', 'template', 'templatePath', ]; @@ -40,14 +40,14 @@ class AjaxView extends AppView { * * @var string */ - protected $subDir = ''; + protected string $subDir = ''; /** * List of special view vars. * * @var array */ - protected $_specialVars = ['_serialize', '_jsonOptions', '_jsonp']; + protected array $_specialVars = ['serialize', '_jsonOptions', '_jsonp']; /** * Constructor @@ -62,7 +62,7 @@ public function __construct( ?ServerRequest $request = null, ?Response $response = null, ?EventManager $eventManager = null, - array $viewOptions = [] + array $viewOptions = [], ) { parent::__construct($request, $response, $eventManager, $viewOptions); @@ -83,7 +83,7 @@ public function __construct( /** * Renders an AJAX view. * The rendered content will be part of the JSON response object and - * can be accessed via response.content in JavaScript. + * can be accessed via `response.content` in JavaScript. * * If an error or success has been set, the rendering will be skipped. * @@ -119,8 +119,8 @@ public function render(?string $view = null, $layout = null): string { } $this->viewVars = Hash::merge($dataToSerialize, $this->viewVars); - if (isset($this->viewVars['_serialize'])) { - $dataToSerialize = $this->_dataToSerialize($this->viewVars['_serialize'], $dataToSerialize); + if (isset($this->viewVars['serialize'])) { + $dataToSerialize = $this->_dataToSerialize($this->viewVars['serialize'], $dataToSerialize); } return $this->_serialize($dataToSerialize); @@ -133,7 +133,7 @@ public function render(?string $view = null, $layout = null): string { * @param array $dataToSerialize Array of data that is to be serialzed. * @return string The serialized data. */ - protected function _serialize(array $dataToSerialize = []) { + protected function _serialize(array $dataToSerialize = []): string { return JsonEncoder::encode($dataToSerialize); } @@ -146,7 +146,7 @@ protected function _serialize(array $dataToSerialize = []) { * render method. * @return array The data to serialize. */ - protected function _dataToSerialize($serialize, $additionalData = []) { + protected function _dataToSerialize(array|bool|string $serialize, array $additionalData = []): array { if ($serialize === true) { $data = array_diff_key( $this->viewVars, diff --git a/src/View/JsonEncoder.php b/src/View/JsonEncoder.php index 4dd361c8..ab7fb683 100644 --- a/src/View/JsonEncoder.php +++ b/src/View/JsonEncoder.php @@ -14,7 +14,7 @@ class JsonEncoder { * * @return string */ - public static function encode(array $dataToSerialize, $options = 0) { + public static function encode(array $dataToSerialize, int $options = 0): string { $result = json_encode($dataToSerialize, $options); $error = null; diff --git a/tests/TestApp/src/Controller/AjaxTestController.php b/tests/TestApp/src/Controller/AjaxTestController.php index 777e31fb..084f0037 100644 --- a/tests/TestApp/src/Controller/AjaxTestController.php +++ b/tests/TestApp/src/Controller/AjaxTestController.php @@ -7,6 +7,7 @@ class AjaxTestController extends Controller { /** + * @throws \Exception * @return void */ public function initialize(): void { diff --git a/tests/TestCase/Controller/Component/AjaxComponentTest.php b/tests/TestCase/Controller/Component/AjaxComponentTest.php index 08b601bb..8ac25a98 100755 --- a/tests/TestCase/Controller/Component/AjaxComponentTest.php +++ b/tests/TestCase/Controller/Component/AjaxComponentTest.php @@ -15,14 +15,14 @@ class AjaxComponentTest extends TestCase { /** * @var array */ - protected $fixtures = [ - 'core.Sessions', + protected array $fixtures = [ + //'core.Sessions', ]; /** * @var \TestApp\Controller\AjaxTestController */ - protected $Controller; + protected AjaxTestController $Controller; /** * @return void @@ -49,6 +49,7 @@ public function testNonAjax() { } /** + * @throws \Exception * @return void */ public function testDefaults() { @@ -96,6 +97,7 @@ public function testDefaults() { } /** + * @throws \Exception * @return void */ public function testAutoDetectOnFalse() { @@ -111,6 +113,7 @@ public function testAutoDetectOnFalse() { } /** + * @throws \Exception * @return void */ public function testActionsInvalid() { @@ -127,6 +130,7 @@ public function testActionsInvalid() { } /** + * @throws \Exception * @return void */ public function testActions() { @@ -143,6 +147,7 @@ public function testActions() { } /** + * @throws \Exception * @return void */ public function testAutoDetectOnFalseViaConfig() { @@ -159,6 +164,7 @@ public function testAutoDetectOnFalseViaConfig() { } /** + * @throws \Exception * @return void */ public function testSetVars() { @@ -170,12 +176,12 @@ public function testSetVars() { $content = ['id' => 1, 'title' => 'title']; $this->Controller->set(compact('content')); - $this->Controller->set('_serialize', ['content']); + $this->Controller->set('serialize', ['content']); $this->Controller->components()->load('Ajax.Ajax'); $this->assertNotEmpty($this->Controller->viewBuilder()->getVars()); - $this->assertNotEmpty($this->Controller->viewBuilder()->getVar('_serialize')); - $this->assertEquals('content', $this->Controller->viewBuilder()->getVar('_serialize')[0]); + $this->assertNotEmpty($this->Controller->viewBuilder()->getVar('serialize')); + $this->assertEquals('content', $this->Controller->viewBuilder()->getVar('serialize')[0]); } /** @@ -189,7 +195,7 @@ public function testSetVarsWithRedirect() { $content = ['id' => 1, 'title' => 'title']; $this->Controller->set(compact('content')); - $this->Controller->set('_serialize', ['content']); + $this->Controller->set('serialize', ['content']); // Let's try a permanent redirect $this->Controller->redirect('/', 301); @@ -211,8 +217,8 @@ public function testSetVarsWithRedirect() { $this->assertArrayHasKey('_message', $this->Controller->viewBuilder()->getVars()); $this->assertNotEmpty($this->Controller->viewBuilder()->getVars()); - $this->assertNotEmpty($this->Controller->viewBuilder()->getVar('_serialize')); - $this->assertTrue(in_array('content', $this->Controller->viewBuilder()->getVar('_serialize'))); + $this->assertNotEmpty($this->Controller->viewBuilder()->getVar('serialize')); + $this->assertTrue(in_array('content', $this->Controller->viewBuilder()->getVar('serialize'))); } } diff --git a/tests/TestCase/View/AjaxViewTest.php b/tests/TestCase/View/AjaxViewTest.php index 253745a6..324fc0bd 100644 --- a/tests/TestCase/View/AjaxViewTest.php +++ b/tests/TestCase/View/AjaxViewTest.php @@ -24,7 +24,7 @@ class AjaxViewTest extends TestCase { /** * @var \Ajax\View\AjaxView */ - protected $Ajax; + protected AjaxView $Ajax; /** * @return void @@ -48,7 +48,7 @@ public function testSerialize() { ['title' => 'Title Two', 'link' => 'http://example.org/two', 'author' => 'two@example.org', 'description' => 'Content two'], ]; $View = new AjaxView($Request, $Response); - $View->set(['items' => $items, '_serialize' => ['items']]); + $View->set(['items' => $items, 'serialize' => ['items']]); $result = $View->render(''); $response = $View->getResponse(); @@ -69,7 +69,7 @@ public function testRenderWithSerialize() { ['title' => 'Title Two', 'link' => 'http://example.org/two', 'author' => 'two@example.org', 'description' => 'Content two'], ]; $View = new AjaxView($Request, $Response); - $View->set(['items' => $items, '_serialize' => 'items']); + $View->set(['items' => $items, 'serialize' => 'items']); $View->setTemplatePath('Items'); $result = $View->render('index'); @@ -81,7 +81,7 @@ public function testRenderWithSerialize() { } /** - * Test the case where the _serialize viewVar is set to true signaling that all viewVars + * Test the case where the `serialize` viewVar is set to true signaling that all viewVars * should be serialized. * * @return void @@ -95,7 +95,7 @@ public function testSerializeSetTrue() { ]; $multiple = 'items'; $View = new AjaxView($Request, $Response); - $View->set(['items' => $items, 'multiple' => $multiple, '_serialize' => true]); + $View->set(['items' => $items, 'multiple' => $multiple, 'serialize' => true]); $result = $View->render(''); $response = $View->getResponse(); @@ -116,7 +116,7 @@ public function testError() { ['title' => 'Title Two', 'link' => 'http://example.org/two', 'author' => 'two@example.org', 'description' => 'Content two'], ]; $View = new AjaxView($Request, $Response); - $View->set(['error' => 'Some message', 'items' => $items, '_serialize' => ['error', 'items']]); + $View->set(['error' => 'Some message', 'items' => $items, 'serialize' => ['error', 'items']]); $View->setTemplatePath('Items'); $result = $View->render('index'); diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 56c6ac8d..c347f9a0 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -3,14 +3,15 @@ use Cake\Cache\Cache; use Cake\Core\Configure; use Cake\Datasource\ConnectionManager; -use Cake\Filesystem\Folder; +use Cake\TestSuite\Fixture\SchemaLoader; +use Shim\Filesystem\Folder; use TestApp\View\AppView; -require dirname(__DIR__) . '/vendor/cakephp/cakephp/src/basics.php'; +require dirname(__DIR__) . '/vendor/cakephp/cakephp/src/functions.php'; require dirname(__DIR__) . '/vendor/autoload.php'; if (!defined('WINDOWS')) { - if (DS == '\\' || substr(PHP_OS, 0, 3) === 'WIN') { + if (DS == '\\' || str_starts_with(PHP_OS, 'WIN')) { define('WINDOWS', true); } else { define('WINDOWS', false); @@ -33,8 +34,9 @@ ini_set('intl.default_locale', 'de-DE'); Configure::write('App', [ - 'namespace' => 'App', - 'encoding' => 'UTF-8']); + 'namespace' => 'App', + 'encoding' => 'UTF-8', +]); Configure::write('debug', true); mb_internal_encoding('UTF-8'); @@ -89,3 +91,8 @@ class_alias(AppView::class, 'App\View\AppView'); 'quoteIdentifiers' => true, 'cacheMetadata' => true, ]); + +if (env('FIXTURE_SCHEMA_METADATA')) { + $loader = new SchemaLoader(); + $loader->loadInternalFile(env('FIXTURE_SCHEMA_METADATA')); +} diff --git a/tests/config/routes.php b/tests/config/routes.php index 5bed995c..1c599220 100644 --- a/tests/config/routes.php +++ b/tests/config/routes.php @@ -2,9 +2,12 @@ namespace Ajax\Test\App\Config; -use Cake\Routing\Router; +use Cake\Routing\RouteBuilder; -Router::scope('/', function($routes) { +/** + * @var \Cake\Routing\RouteBuilder $routes + */ +$routes->scope('/', function (RouteBuilder $routes) { $routes->connect('/:controller', ['action' => 'index'], ['routeClass' => 'DashedRoute']); $routes->connect('/:controller/:action/*', [], ['routeClass' => 'DashedRoute']); });