diff --git a/composer.json b/composer.json index 934accac..944695e5 100644 --- a/composer.json +++ b/composer.json @@ -31,7 +31,7 @@ "require":{ "php": ">=7.2.0", "cakephp/cakephp": "^4.0.2", - "friendsofcake/crud": "^6.0-beta", + "friendsofcake/crud": "^6.0-beta3", "neomerx/json-api": "^4.0" }, "require-dev": { @@ -66,7 +66,7 @@ "cs-check": "phpcs -p --standard=vendor/cakephp/cakephp-codesniffer/CakePHP src/ tests/", "cs-fix": "phpcbf --standard=vendor/cakephp/cakephp-codesniffer/CakePHP src/ tests/", "phpstan-setup": "cp composer.json composer.backup && composer require --dev phpstan/phpstan:\"^0.12\" && mv composer.backup composer.json", - "phpstan": "phpstan analyse src/", + "phpstan": "phpstan analyse --memory-limit=3G src/", "test": "phpunit" } } diff --git a/phpstan.neon b/phpstan.neon index 2bc16328..a85d75fc 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -9,9 +9,6 @@ parameters: ignoreErrors: # ORM - '#Call to an undefined method Cake\\Datasource\\EntityInterface::visibleProperties\(\)\.#' - - '#Call to an undefined method Cake\\ORM\\Association::getAlias\(\)\.#' - - '#Method CrudJsonApi\\Listener\\JsonApiListener::_getSingleEntity\(\) should return Cake\\Datasource\\EntityInterface but returns array\|Cake\\Datasource\\EntityInterface\|null\.#' - - '#Method CrudJsonApi\\Listener\\JsonApiListener::_getSingleEntity\(\) should return Cake\\Datasource\\EntityInterface but returns array\|object\|null\.#' # Crud properties - '#Access to an undefined property object::\$created#' diff --git a/src/Listener/JsonApi/DocumentValidator.php b/src/Listener/JsonApi/DocumentValidator.php index d834ab81..acb38107 100644 --- a/src/Listener/JsonApi/DocumentValidator.php +++ b/src/Listener/JsonApi/DocumentValidator.php @@ -259,7 +259,7 @@ protected function _primaryDataMayHaveRelationships(): bool if (empty($relationships)) { $this->_errorCollection->addRelationshipsError( $title = '_required', - $detail = "Relationships object does not contain any members", + $detail = 'Relationships object does not contain any members', $status = null, $idx = null, $aboutLink = $this->_getAboutLink('http://jsonapi.org/format/#crud-creating') diff --git a/src/Listener/JsonApiListener.php b/src/Listener/JsonApiListener.php index 30c2b5c3..b69bc4d9 100644 --- a/src/Listener/JsonApiListener.php +++ b/src/Listener/JsonApiListener.php @@ -3,13 +3,13 @@ namespace CrudJsonApi\Listener; -use Cake\Core\Configure; use Cake\Datasource\EntityInterface; use Cake\Datasource\RepositoryInterface; use Cake\Datasource\ResultSetDecorator; use Cake\Datasource\ResultSetInterface; use Cake\Event\EventInterface; use Cake\Http\Exception\BadRequestException; +use Cake\Http\Exception\MethodNotAllowedException; use Cake\Http\Response; use Cake\ORM\Association; use Cake\ORM\Query; @@ -110,28 +110,6 @@ public function implementedEvents(): array ]; } - /** - * setup - * - * Called when the listener is created - * - * @return void - */ - public function setup(): void - { - if (!$this->_checkRequestType('jsonapi')) { - return; - } - - $appClass = Configure::read('App.namespace') . '\Application'; - - // If `App\Application` class exists it means Cake 3.3's PSR7 middleware - // implementation is used and it's too late to register new error handler. - if (!class_exists($appClass, false)) { - $this->registerExceptionHandler(); - } - } - /** * beforeHandle * @@ -435,7 +413,7 @@ protected function _includeParameter($includes, Subject $subject, $options): voi } if ($options['blacklist'] === true || $options['whitelist'] === false) { - throw new BadRequestException("The include parameter is not supported"); + throw new BadRequestException('The include parameter is not supported'); } $this->setConfig('include', []); @@ -790,16 +768,16 @@ protected function _validateConfigOptions(): void * Override ApiListener method to enforce required JSON API request methods. * * @throws \Cake\Http\Exception\BadRequestException - * @return bool + * @return void */ - protected function _checkRequestMethods(): bool + protected function _checkRequestMethods(): void { if ($this->_request()->is('put')) { - throw new BadRequestException('JSON API does not support the PUT method, use PATCH instead'); + throw new MethodNotAllowedException('JSON API does not support the PUT method, use PATCH instead'); } if (!$this->_request()->contentType()) { - return true; + return; } if ($this->_request()->contentType() !== self::MIME_TYPE) { @@ -807,8 +785,6 @@ protected function _checkRequestMethods(): bool 'JSON API requests with data require the "' . self::MIME_TYPE . '" Content-Type header' ); } - - return true; } /** @@ -866,11 +842,11 @@ protected function _getFindResult(Subject $subject) * * @param \Crud\Event\Subject $subject Subject * @return \Cake\Datasource\EntityInterface|null - * @phpstan-ignore */ protected function _getSingleEntity(Subject $subject): ?EntityInterface { if (!empty($subject->entities) && $subject->entities instanceof Query) { + // @phpstan-ignore-next-line return (clone $subject->entities)->first(); } @@ -987,7 +963,7 @@ protected function _getRepositoryList(RepositoryInterface $repository, array $as ]; if ($association['association'] === null) { - throw new InvalidArgumentException("Association does not have an association object set"); + throw new InvalidArgumentException('Association does not have an association object set'); } $associationRepository = $association['association']->getTarget(); diff --git a/src/Schema/JsonApi/DynamicEntitySchema.php b/src/Schema/JsonApi/DynamicEntitySchema.php index c0ab0347..1274c2fc 100644 --- a/src/Schema/JsonApi/DynamicEntitySchema.php +++ b/src/Schema/JsonApi/DynamicEntitySchema.php @@ -356,13 +356,13 @@ public function getRelationshipRelatedLink($entity, string $name): LinkInterface // generate the link for hasMany relationship $foreignKeys = (array)$association->getForeignKey(); $primaryKeys = $entity->extract((array)$this->getRepository()->getPrimaryKey()); - $keys = array_combine($foreignKeys, $primaryKeys); + $keys = (array)array_combine($foreignKeys, $primaryKeys); $url = Router::url( $this->_getRepositoryRoutingParameters($relatedRepository) + $keys + [ '_method' => 'GET', 'action' => 'index', - '?' => $keys + '?' => $keys, ], $this->view->getConfig('absoluteLinks', false) ); diff --git a/tests/TestCase/Listener/JsonApiListenerTest.php b/tests/TestCase/Listener/JsonApiListenerTest.php index 71bd70db..c67e4086 100644 --- a/tests/TestCase/Listener/JsonApiListenerTest.php +++ b/tests/TestCase/Listener/JsonApiListenerTest.php @@ -9,6 +9,7 @@ use Cake\Event\Event; use Cake\Filesystem\File; use Cake\Http\Exception\BadRequestException; +use Cake\Http\Exception\MethodNotAllowedException; use Cake\Http\Response; use Cake\Http\ServerRequest; use Cake\ORM\Entity; @@ -192,10 +193,6 @@ public function testBeforeHandle() ->method('_convertJsonApiDocumentArray') ->willReturn([]); - $listener - ->method('_checkRequestMethods') - ->willReturn(true); - $listener->beforeHandle(new Event('Crud.beforeHandle')); $this->assertTrue(true); } @@ -737,7 +734,8 @@ public function testCheckRequestMethodsSuccess() $listener->setupDetectors(); $this->setReflectionClassInstance($listener); - $this->assertTrue($this->callProtectedMethod('_checkRequestMethods', [], $listener)); + $this->callProtectedMethod('_checkRequestMethods', [], $listener); + $this->assertTrue(true); //No exception was thrown } /** @@ -745,7 +743,7 @@ public function testCheckRequestMethodsSuccess() */ public function testCheckRequestMethodsFailContentHeader() { - $this->expectException('Cake\Http\Exception\BadRequestException'); + $this->expectException(BadRequestException::class); $this->expectExceptionMessage('JSON API requests with data require the "application/vnd.api+json" Content-Type header'); $request = new ServerRequest(); $request = $request->withEnv('HTTP_ACCEPT', 'application/vnd.api+json') @@ -765,7 +763,7 @@ public function testCheckRequestMethodsFailContentHeader() */ public function testCheckRequestMethodsFailOnPutMethod() { - $this->expectException('Cake\Http\Exception\BadRequestException'); + $this->expectException(MethodNotAllowedException::class); $this->expectExceptionMessage('JSON API does not support the PUT method, use PATCH instead'); $request = new ServerRequest(); $request = $request->withEnv('HTTP_ACCEPT', 'application/vnd.api+json') @@ -1148,10 +1146,6 @@ public function testCheckRequestData() ->method('_convertJsonApiDocumentArray') ->willReturn([]); - $listener - ->method('_checkRequestMethods') - ->willReturn(true); - $this->setReflectionClassInstance($listener); // assert null if there is no Content-Type diff --git a/tests/test_app/src/Model/Table/CountriesTable.php b/tests/test_app/src/Model/Table/CountriesTable.php index 168a4dcd..3ba575b2 100644 --- a/tests/test_app/src/Model/Table/CountriesTable.php +++ b/tests/test_app/src/Model/Table/CountriesTable.php @@ -46,7 +46,7 @@ public function validationDefault(Validator $validator): Validator ->add('code', [ 'UPPERCASE_ONLY' => [ 'rule' => ['custom', '/^([A-Z]+)+$/'], - 'message' => "Field must be uppercase only", + 'message' => 'Field must be uppercase only', ], ]);