diff --git a/.gitattributes b/.gitattributes
index 3461ac8..9eeb392 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -12,11 +12,10 @@ tests export-ignore
.editorconfig export-ignore
.gitattributes export-ignore
.gitignore export-ignore
-.stickler.yml export-ignore
-.travis.yml export-ignore
-phpcs.xml.dist export-ignore
+phpcs.xml export-ignore
phpstan.neon export-ignore
phpstan-baseline.neon export-ignore
phpunit.xml.dist export-ignore
psalm.xml export-ignore
psalm-baseline.xml export-ignore
+.phive export-ignore
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 9ea52ef..2e410c8 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -4,105 +4,19 @@ on:
push:
branches:
- 4.x
+ - 5.x
pull_request:
branches:
- '*'
+permissions:
+ contents: read
+
jobs:
testsuite:
- runs-on: ubuntu-22.04
- strategy:
- fail-fast: false
- matrix:
- php-version: ['7.4', '8.2']
- prefer-lowest: ['']
- include:
- - php-version: '7.4'
- prefer-lowest: 'prefer-lowest'
-
- steps:
- - uses: actions/checkout@v3
-
- - name: Setup PHP
- uses: shivammathur/setup-php@v2
- with:
- php-version: ${{ matrix.php-version }}
- extensions: mbstring, intl
- coverage: pcov
-
- - name: Get composer cache directory
- id: composer-cache
- run: echo "::set-output name=dir::$(composer config cache-files-dir)"
-
- - name: Get date part for cache key
- id: key-date
- run: echo "::set-output name=date::$(date +'%Y-%m')"
-
- - name: Cache composer dependencies
- uses: actions/cache@v3
- with:
- path: ${{ steps.composer-cache.outputs.dir }}
- key: ${{ runner.os }}-composer-${{ steps.key-date.outputs.date }}-${{ hashFiles('composer.json') }}-${{ matrix.prefer-lowest }}
-
- - name: Composer Install
- run: |
- if ${{ matrix.prefer-lowest == 'prefer-lowest' }}; then
- composer update --prefer-lowest --prefer-stable
- else
- composer update
- fi
-
- - name: Configure PHPUnit matcher
- if: matrix.php-version == '7.4'
- uses: mheap/phpunit-matcher-action@main
-
- - name: Run PHPUnit
- run: |
- if [[ ${{ matrix.php-version }} == '7.4' ]]; then
- export CODECOVERAGE=1 && vendor/bin/phpunit --verbose --coverage-clover=coverage.xml
- else
- vendor/bin/phpunit
- fi
-
- - name: Submit code coverage
- if: success() && matrix.php-version == '7.4'
- uses: codecov/codecov-action@v3
+ uses: cakephp/.github/.github/workflows/testsuite-with-db.yml@5.x
+ secrets: inherit
cs-stan:
- name: Coding Standard & Static Analysis
- runs-on: ubuntu-22.04
-
- steps:
- - uses: actions/checkout@v3
-
- - name: Setup PHP
- uses: shivammathur/setup-php@v2
- with:
- php-version: '7.4'
- extensions: mbstring, intl
- tools: cs2pr
- coverage: none
-
- - name: Get composer cache directory
- id: composer-cache
- run: echo "::set-output name=dir::$(composer config cache-files-dir)"
-
- - name: Get date part for cache key
- id: key-date
- run: echo "::set-output name=date::$(date +'%Y-%m')"
-
- - name: Cache composer dependencies
- uses: actions/cache@v3
- with:
- path: ${{ steps.composer-cache.outputs.dir }}
- key: ${{ runner.os }}-composer-${{ steps.key-date.outputs.date }}-${{ hashFiles('composer.json') }}-${{ matrix.prefer-lowest }}
-
- - name: Composer install
- run: composer stan-setup
-
- - name: Run PHP CodeSniffer
- run: composer cs-check
-
- - name: Run phpstan
- if: success() || failure()
- run: vendor/bin/phpstan analyse --error-format=github
+ uses: cakephp/.github/.github/workflows/cs-stan.yml@5.x
+ secrets: inherit
diff --git a/.phive/phars.xml b/.phive/phars.xml
new file mode 100644
index 0000000..d2fe06f
--- /dev/null
+++ b/.phive/phars.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/.stickler.yml b/.stickler.yml
deleted file mode 100644
index b8f57db..0000000
--- a/.stickler.yml
+++ /dev/null
@@ -1,10 +0,0 @@
----
-linters:
- phpcs:
- standard: CakePHP4
- extensions: 'php'
- fixer: true
-
-fixers:
- enable: true
- workflow: commit
diff --git a/README.md b/README.md
index 18c5d80..caa0f5e 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@
Quickly enable CSV output of your model data.
-This branch is for CakePHP **4.x**. For details see [version map](https://github.com/FriendsOfCake/cakephp-csvview/wiki#cakephp-version-map).
+This branch is for CakePHP **5.x**. For details see [version map](https://github.com/FriendsOfCake/cakephp-csvview/wiki#cakephp-version-map).
## Background
@@ -182,18 +182,22 @@ to set how null values should be displayed in the CSV.
#### Automatic view class switching
-You can use router's extension parsing feature and the `RequestHandlerComponent` to
-automatically have the CsvView class switched in as follows.
+You can use the controller's content negotiation feature to automatically have
+the CsvView class switched in as follows.
-Enable `csv` extension parsing for all routes using `Router::extensions('csv')`
-in your app's `routes.php` or using `$routes->addExtensions()` within required
-scope.
+Enable `csv` extension parsing using `$routes->addExtensions(['csv'])` within required
+scope in your app's `routes.php`.
```php
// PostsController.php
-// In your controller's initialize() method:
-$this->loadComponent('RequestHandler');
+// Add the CsvView class for content type negotiation
+public function initialize(): void
+{
+ parent::initialize();
+
+ $this->addViewClasses(['csv' => 'CsvView.Csv']);
+}
// Controller action
public function index()
diff --git a/composer.json b/composer.json
index d401ee2..c0bbb6a 100644
--- a/composer.json
+++ b/composer.json
@@ -18,38 +18,37 @@
"role": "Maintainer"
},
{
- "name":"ADmad",
- "role":"Contributor",
+ "name": "ADmad",
+ "role": "Contributor",
"homepage": "https://github.com/admad"
},
{
- "name":"Mark Scherer",
- "role":"Contributor",
+ "name": "Mark Scherer",
+ "role": "Contributor",
"homepage": "https://github.com/dereuromark"
},
{
- "name":"Joshua Paling",
- "role":"Contributor",
+ "name": "Joshua Paling",
+ "role": "Contributor",
"homepage": "https://github.com/joshuapaling"
},
{
- "name":"Gaurish Sharma",
- "role":"Contributor",
+ "name": "Gaurish Sharma",
+ "role": "Contributor",
"homepage": "https://github.com/gaurish"
},
{
- "name":"Gregory Gaskill",
- "role":"Contributor",
- "homepage":"https://github.com/chronon"
+ "name": "Gregory Gaskill",
+ "role": "Contributor",
+ "homepage": "https://github.com/chronon"
}
],
- "require":{
- "php": ">=7.4",
- "cakephp/cakephp": "^4.2"
+ "require": {
+ "cakephp/cakephp": "^5.0"
},
"require-dev": {
- "phpunit/phpunit": "^9.5.0",
- "cakephp/cakephp-codesniffer": "^4.0"
+ "phpunit/phpunit": "^10.1",
+ "cakephp/cakephp-codesniffer": "^5.0"
},
"autoload": {
"psr-4": {
diff --git a/phpcs.xml b/phpcs.xml
index ca08438..e747af8 100644
--- a/phpcs.xml
+++ b/phpcs.xml
@@ -1,7 +1,7 @@
-
+
-
+
src/
tests/
diff --git a/phpstan.neon b/phpstan.neon
index 91bea65..89d9c92 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -1,6 +1,7 @@
parameters:
level: 8
checkMissingIterableValueType: false
+ checkGenericClassInNonGenericObjectType: false
paths:
- src/
bootstrapFiles:
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 9910dce..b3a3168 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -1,32 +1,19 @@
-
-
-
+
- tests/TestCase/
+ tests/TestCase/
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
+
+
diff --git a/src/Plugin.php b/src/CsvViewPlugin.php
similarity index 54%
rename from src/Plugin.php
rename to src/CsvViewPlugin.php
index 33a783c..e3755c3 100644
--- a/src/Plugin.php
+++ b/src/CsvViewPlugin.php
@@ -5,50 +5,36 @@
use Cake\Core\BasePlugin;
use Cake\Core\PluginApplicationInterface;
-use Cake\Event\EventInterface;
-use Cake\Event\EventManager;
use Cake\Http\ServerRequest;
-class Plugin extends BasePlugin
+class CsvViewPlugin extends BasePlugin
{
/**
* Plugin name.
*
* @var string
*/
- protected $name = 'CsvView';
+ protected ?string $name = 'CsvView';
/**
* Load routes or not
*
* @var bool
*/
- protected $routesEnabled = false;
+ protected bool $routesEnabled = false;
/**
* Console middleware
*
* @var bool
*/
- protected $consoleEnabled = false;
+ protected bool $consoleEnabled = false;
/**
* @inheritDoc
*/
public function bootstrap(PluginApplicationInterface $app): void
{
- /**
- * Add CsvView to View class map through RequestHandler, if available, on Controller initialisation
- *
- * @link https://book.cakephp.org/4/en/controllers/components/request-handling.html#using-custom-viewclasses
- */
- EventManager::instance()->on('Controller.initialize', function (EventInterface $event) {
- $controller = $event->getSubject();
- if ($controller->components()->has('RequestHandler')) {
- $controller->RequestHandler->setConfig('viewClassMap.csv', 'CsvView.Csv');
- }
- });
-
/**
* Add a request detector named "csv" to check whether the request was for a CSV,
* either through accept header or file extension
diff --git a/src/View/CsvView.php b/src/View/CsvView.php
index 5c6ebf9..88b1d3b 100644
--- a/src/View/CsvView.php
+++ b/src/View/CsvView.php
@@ -7,7 +7,6 @@
use Cake\Datasource\EntityInterface;
use Cake\Utility\Hash;
use Cake\View\SerializedView;
-use Exception;
/**
* A view class that is used for CSV responses.
@@ -67,7 +66,7 @@ class CsvView extends SerializedView
*
* @var string
*/
- protected $layoutPath = 'csv';
+ protected string $layoutPath = 'csv';
/**
* CSV views are always located in the 'csv' sub directory for a
@@ -75,21 +74,14 @@ class CsvView extends SerializedView
*
* @var string
*/
- protected $subDir = 'csv';
-
- /**
- * Response type.
- *
- * @var string
- */
- protected $_responseType = 'text/csv';
+ protected string $subDir = 'csv';
/**
* Whether or not to reset static variables in use
*
* @var bool
*/
- protected $_resetStaticVariables = false;
+ protected bool $_resetStaticVariables = false;
/**
* Iconv extension.
@@ -110,14 +102,14 @@ class CsvView extends SerializedView
*
* @var array
*/
- protected $bomMap;
+ protected array $bomMap;
/**
* BOM first appearance
*
* @var bool
*/
- protected $isFirstBom = true;
+ protected bool $isFirstBom = true;
/**
* Default config.
@@ -146,7 +138,7 @@ class CsvView extends SerializedView
*
* @var array
*/
- protected $_defaultConfig = [
+ protected array $_defaultConfig = [
'extract' => null,
'footer' => null,
'header' => null,
@@ -188,6 +180,16 @@ public function initialize(): void
parent::initialize();
}
+ /**
+ * Mime-type this view class renders as.
+ *
+ * @return string The CSV content type.
+ */
+ public static function contentType(): string
+ {
+ return 'text/csv';
+ }
+
/**
* Serialize view vars.
*
@@ -195,7 +197,7 @@ public function initialize(): void
* need(s) to be serialized
* @return string The serialized data or false.
*/
- protected function _serialize($serialize): string
+ protected function _serialize(array|string $serialize): string
{
$this->_renderRow($this->getConfig('header'));
$this->_renderContent();
@@ -211,7 +213,7 @@ protected function _serialize($serialize): string
* Renders the body of the data to the csv
*
* @return void
- * @throws \Exception
+ * @throws \Cake\Core\Exception\CakeException
*/
protected function _renderContent(): void
{
@@ -224,7 +226,7 @@ protected function _renderContent(): void
foreach ((array)$serialize as $viewVar) {
if (is_scalar($this->viewVars[$viewVar])) {
- throw new Exception("'" . $viewVar . "' is not an array or iteratable object.");
+ throw new CakeException("'" . $viewVar . "' is not an array or iteratable object.");
}
foreach ($this->viewVars[$viewVar] as $_data) {
@@ -296,7 +298,7 @@ protected function _renderRow(?array $row = null): string
* @param array|null $row Row data
* @return string|false String with the row in csv-syntax, false on fputscv failure
*/
- protected function _generateRow(?array $row = null)
+ protected function _generateRow(?array $row = null): string|false
{
static $fp = false;
diff --git a/tests/Fixture/ArticlesFixture.php b/tests/Fixture/ArticlesFixture.php
new file mode 100644
index 0000000..ba27cd2
--- /dev/null
+++ b/tests/Fixture/ArticlesFixture.php
@@ -0,0 +1,13 @@
+ 1, 'title' => 'First Article', 'body' => 'First Article Body', 'published' => 'Y'],
+ ['author_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body', 'published' => 'Y'],
+ ['author_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body', 'published' => 'Y'],
+ ];
+}
diff --git a/tests/Fixture/AuthorsFixture.php b/tests/Fixture/AuthorsFixture.php
new file mode 100644
index 0000000..24ef38d
--- /dev/null
+++ b/tests/Fixture/AuthorsFixture.php
@@ -0,0 +1,14 @@
+ 'mariano'],
+ ['name' => 'nate'],
+ ['name' => 'larry'],
+ ['name' => 'garrett'],
+ ];
+}
diff --git a/tests/TestCase/View/CsvViewTest.php b/tests/TestCase/View/CsvViewTest.php
index ebaab48..4dad49b 100644
--- a/tests/TestCase/View/CsvViewTest.php
+++ b/tests/TestCase/View/CsvViewTest.php
@@ -5,17 +5,17 @@
use Cake\Http\Response;
use Cake\Http\ServerRequest as Request;
-use Cake\I18n\FrozenTime;
-use Cake\ORM\TableRegistry;
+use Cake\I18n\DateTime;
use Cake\TestSuite\TestCase;
use CsvView\View\CsvView;
+use Exception;
/**
* CsvViewTest
*/
class CsvViewTest extends TestCase
{
- public $fixtures = ['core.Articles', 'core.Authors'];
+ protected array $fixtures = ['plugin.CsvView.Articles', 'plugin.CsvView.Authors'];
/**
* @var \CsvView\View\CsvView
@@ -34,7 +34,9 @@ class CsvViewTest extends TestCase
public function setUp(): void
{
- FrozenTime::setToStringFormat('yyyy-MM-dd HH:mm:ss');
+ parent::setUp();
+
+ DateTime::setToStringFormat('yyyy-MM-dd HH:mm:ss');
$this->request = new Request();
$this->response = new Response();
@@ -267,7 +269,7 @@ public function testRenderViaExtract()
[
'User' => [
'username' => 'jose',
- 'created' => new FrozenTime('2010-01-05'),
+ 'created' => new DateTime('2010-01-05'),
],
'Item' => [
'name' => 'beach',
@@ -343,7 +345,7 @@ public function testRenderViaExtractWithCallable()
$data = [
[
'username' => 'jose',
- 'created' => new FrozenTime('2010-01-05'),
+ 'created' => new DateTime('2010-01-05'),
'item' => [
'name' => 'beach',
],
@@ -431,7 +433,7 @@ public function testRenderWithSpecialCharacters()
*/
public function testPassingQueryAsData()
{
- $articles = TableRegistry::getTableLocator()->get('Articles');
+ $articles = $this->getTableLocator()->get('Articles');
$query = $articles->find();
$this->view->set(['data' => $query])
@@ -439,7 +441,7 @@ public function testPassingQueryAsData()
$output = $this->view->render();
$articles->belongsTo('Authors');
- $query = $articles->find('all', ['contain' => 'Authors']);
+ $query = $articles->find('all', contain: 'Authors');
$_extract = ['title', 'body', 'author.name'];
$this->view->set(['data' => $query])
->setConfig(['extract' => $_extract, 'serialize' => 'data']);
@@ -511,7 +513,7 @@ public function testRenderWithCustomNull()
*/
public function testInvalidViewVarThrowsException()
{
- $this->expectException(\Exception::class);
+ $this->expectException(Exception::class);
$this->view->set(['data' => 'invaliddata']);
$this->view->setConfig('serialize', 'data');
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
index f2f2e40..168d2bf 100644
--- a/tests/bootstrap.php
+++ b/tests/bootstrap.php
@@ -2,6 +2,7 @@
declare(strict_types=1);
use Cake\Core\Configure;
+use Cake\TestSuite\Fixture\SchemaLoader;
/**
* Test suite bootstrap
@@ -37,7 +38,13 @@
}
Configure::write('App', [
'namespace' => 'CsvView\Test\App',
+ 'encoding' => 'UTF-8',
'paths' => [
'templates' => [dirname(__FILE__) . DS . 'test_app' . DS . 'templates' . DS],
],
]);
+
+if (getenv('FIXTURE_SCHEMA_METADATA')) {
+ $loader = new SchemaLoader();
+ $loader->loadInternalFile(getenv('FIXTURE_SCHEMA_METADATA'));
+}
diff --git a/tests/schema.php b/tests/schema.php
new file mode 100644
index 0000000..93062fe
--- /dev/null
+++ b/tests/schema.php
@@ -0,0 +1,53 @@
+ [
+ 'columns' => [
+ 'id' => [
+ 'type' => 'integer',
+ ],
+ 'author_id' => [
+ 'type' => 'integer',
+ 'null' => true,
+ ],
+ 'title' => [
+ 'type' => 'string',
+ 'null' => true,
+ ],
+ 'body' => 'text',
+ 'published' => [
+ 'type' => 'string',
+ 'length' => 1,
+ 'default' => 'N',
+ ],
+ ],
+ 'constraints' => [
+ 'primary' => [
+ 'type' => 'primary',
+ 'columns' => [
+ 'id',
+ ],
+ ],
+ ],
+ ],
+ 'authors' => [
+ 'columns' => [
+ 'id' => [
+ 'type' => 'integer',
+ ],
+ 'name' => [
+ 'type' => 'string',
+ 'default' => null,
+ ],
+ ],
+ 'constraints' => [
+ 'primary' => [
+ 'type' => 'primary',
+ 'columns' => [
+ 'id',
+ ],
+ ],
+ ],
+ ],
+];
diff --git a/tests/test_app/TestApp/Application.php b/tests/test_app/TestApp/Application.php
index fca4cdc..d999115 100644
--- a/tests/test_app/TestApp/Application.php
+++ b/tests/test_app/TestApp/Application.php
@@ -18,6 +18,7 @@
use Cake\Error\Middleware\ErrorHandlerMiddleware;
use Cake\Http\BaseApplication;
+use Cake\Http\MiddlewareQueue;
use Cake\Routing\Middleware\AssetMiddleware;
use Cake\Routing\Middleware\RoutingMiddleware;
@@ -49,7 +50,7 @@ public function bootstrap(): void
* @param \Cake\Http\MiddlewareQueue $middlewareQueue The middleware queue to setup.
* @return \Cake\Http\MiddlewareQueue The updated middleware queue.
*/
- public function middleware(\Cake\Http\MiddlewareQueue $middlewareQueue): \Cake\Http\MiddlewareQueue
+ public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue
{
$middlewareQueue
// Catch any exceptions in the lower layers,