From 4a0dc367692a6c78ca051b04aef9f3397ceab917 Mon Sep 17 00:00:00 2001
From: Juan Pablo Ramirez <pabloelcolombiano@gmail.com>
Date: Wed, 10 Mar 2021 15:47:09 +0100
Subject: [PATCH] Handle non conventional controller names

---
 .gitignore                                    |   1 +
 README.md                                     |   9 +-
 .../Component/ApiPaginationComponent.php      |   3 +-
 ...entOnNonConventionalControllerNameTest.php | 113 ++++++++++++++++++
 .../Component/ApiPaginationComponentTest.php  |   2 +-
 .../Controller/ArticlesIndexController.php    |  15 +++
 6 files changed, 139 insertions(+), 4 deletions(-)
 create mode 100644 tests/TestCase/Controller/Component/ApiPaginationComponentOnNonConventionalControllerNameTest.php
 create mode 100644 tests/test_app/TestApp/Controller/ArticlesIndexController.php

diff --git a/.gitignore b/.gitignore
index b6bd0e7..6bbc55f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@ build
 composer.lock
 vendor
 .phpunit.result.cache
+.idea
diff --git a/README.md b/README.md
index 6e4fc83..b4303ea 100644
--- a/README.md
+++ b/README.md
@@ -73,7 +73,7 @@ and will look something like this:
 
 ### Configuring the Pagination Output
 
-ApiPagination has three keys for configuration: `key`, `aliases`, and `visible`.
+ApiPagination has four keys for configuration: `key`, `aliases`, `visible` and `model`.
 
 * `key` allows you to change the name of the pagination key.
 
@@ -83,6 +83,10 @@ ApiPagination has three keys for configuration: `key`, `aliases`, and `visible`.
   response. **Note:** Whenever setting a key's visibility, make sure to use the
   aliased name if you've given it one.
 
+* `model` allows you to set the name of the model the pagination is applied on
+  if the controller does not follow CakePHP conventions, e.g. `ArticlesIndexController`.
+  Per default the model is the name of the controller, e.g. `Articles` for `ArticlesController`.
+
 An example using all these configuration keys:
 
 ``` php
@@ -97,7 +101,8 @@ $this->loadComponent('BryanCrowe/ApiPagination.ApiPagination', [
         'resultCount',
         'prevPage',
         'nextPage'
-    ]
+    ],
+    'model' => 'Articles',
 ]);
 ```
 
diff --git a/src/Controller/Component/ApiPaginationComponent.php b/src/Controller/Component/ApiPaginationComponent.php
index 0d700f3..8a4b938 100644
--- a/src/Controller/Component/ApiPaginationComponent.php
+++ b/src/Controller/Component/ApiPaginationComponent.php
@@ -45,7 +45,8 @@ public function beforeRender(Event $event)
         }
 
         $subject = $event->getSubject();
-        $this->pagingInfo = $this->getController()->getRequest()->getAttribute('paging')[$subject->getName()];
+        $modelName = ucfirst($this->getConfig('model', $subject->getName()));
+        $this->pagingInfo = $this->getController()->getRequest()->getAttribute('paging')[$modelName];
         $config = $this->getConfig();
 
         if (!empty($config['aliases'])) {
diff --git a/tests/TestCase/Controller/Component/ApiPaginationComponentOnNonConventionalControllerNameTest.php b/tests/TestCase/Controller/Component/ApiPaginationComponentOnNonConventionalControllerNameTest.php
new file mode 100644
index 0000000..39a6c67
--- /dev/null
+++ b/tests/TestCase/Controller/Component/ApiPaginationComponentOnNonConventionalControllerNameTest.php
@@ -0,0 +1,113 @@
+<?php
+declare(strict_types=1);
+
+namespace BryanCrowe\ApiPagination\Test;
+
+use BryanCrowe\ApiPagination\Controller\Component\ApiPaginationComponent;
+use BryanCrowe\ApiPagination\TestApp\Controller\ArticlesIndexController;
+use Cake\Event\Event;
+use Cake\Http\ServerRequest as Request;
+use Cake\ORM\TableRegistry;
+use Cake\TestSuite\TestCase;
+
+/**
+ * ApiPaginationComponentTest class
+ *
+ * @property ArticlesIndexController $controller
+ */
+class ApiPaginationComponentOnNonConventionalControllerNameTest extends TestCase
+{
+    public $fixtures = ['plugin.BryanCrowe/ApiPagination.Articles'];
+
+    /**
+     * setUp method
+     *
+     * @return void
+     */
+    public function setUp(): void
+    {
+        $this->request = new Request(['url' => '/articles']);
+        $this->response = $this->createMock('Cake\Http\Response');
+        $this->controller = new ArticlesIndexController($this->request, $this->response);
+        $this->Articles = TableRegistry::getTableLocator()->get('BryanCrowe/ApiPagination.Articles', ['table' => 'bryancrowe_articles']);
+        parent::setUp();
+    }
+
+    /**
+     * tearDown method
+     *
+     * @return void
+     */
+    public function tearDown(): void
+    {
+        parent::tearDown();
+    }
+
+    /**
+     * Test that a non conventional controller name is supported using the 'model' config.
+     *
+     * @dataProvider dataForTestVariousModelValueOnNonConventionalController
+     * @param array $config
+     * @param $expected
+     * @return void
+     */
+    public function testVariousModelValueOnNonConventionalController(array $config, $expected)
+    {
+        $this->controller->setRequest(
+            $this->controller->getRequest()->withEnv('HTTP_ACCEPT', 'application/json')
+        );
+        $this->controller->set('data', $this->controller->paginate($this->Articles));
+        $apiPaginationComponent = new ApiPaginationComponent($this->controller->components(), $config);
+        $event = new Event('Controller.beforeRender', $this->controller);
+        $apiPaginationComponent->beforeRender($event);
+
+        $result = $apiPaginationComponent->getController()->viewBuilder()->getVar('pagination');
+        $this->assertSame($expected, $result);
+    }
+
+    /**
+     * If the name of the paginated model is not specified, the result of the pagination
+     * on a controller not having the same name as the model fails.
+     *
+     * @return array[]
+     */
+    public function dataForTestVariousModelValueOnNonConventionalController(): array
+    {
+        return [
+
+            [[], null],
+            [['model' => 'Articles'], $this->getDefaultPagination()],
+            [['model' => 'articles'], $this->getDefaultPagination()],
+            [['model' => 'NonExistingModel'], null],
+        ];
+    }
+
+    /**
+     * Returns the standard pagination result.
+     *
+     * @return array
+     */
+    private function getDefaultPagination(): array
+    {
+        return [
+            'count' => 23,
+            'current' => 20,
+            'perPage' => 20,
+            'page' => 1,
+            'requestedPage' => 1,
+            'pageCount' => 2,
+            'start' => 1,
+            'end' => 20,
+            'prevPage' => false,
+            'nextPage' => true,
+            'sort' => null,
+            'direction' => null,
+            'sortDefault' => false,
+            'directionDefault' => false,
+            'completeSort' => [],
+            'limit' => null,
+            'scope' => null,
+            'finder' => 'all',
+        ];
+    }
+}
diff --git a/tests/TestCase/Controller/Component/ApiPaginationComponentTest.php b/tests/TestCase/Controller/Component/ApiPaginationComponentTest.php
index ddb7f95..2d13563 100644
--- a/tests/TestCase/Controller/Component/ApiPaginationComponentTest.php
+++ b/tests/TestCase/Controller/Component/ApiPaginationComponentTest.php
@@ -29,7 +29,7 @@ public function setUp(): void
         $this->request = new Request(['url' => '/articles']);
         $this->response = $this->createMock('Cake\Http\Response');
         $this->controller = new ArticlesController($this->request, $this->response);
-        $this->Articles = TableRegistry::get('BryanCrowe/ApiPagination.Articles', ['table' => 'bryancrowe_articles']);
+        $this->Articles = TableRegistry::getTableLocator()->get('BryanCrowe/ApiPagination.Articles', ['table' => 'bryancrowe_articles']);
         parent::setUp();
     }
 
diff --git a/tests/test_app/TestApp/Controller/ArticlesIndexController.php b/tests/test_app/TestApp/Controller/ArticlesIndexController.php
new file mode 100644
index 0000000..a999113
--- /dev/null
+++ b/tests/test_app/TestApp/Controller/ArticlesIndexController.php
@@ -0,0 +1,15 @@
+<?php
+declare(strict_types=1);
+
+namespace BryanCrowe\ApiPagination\TestApp\Controller;
+
+use Cake\Controller\Controller;
+
+class ArticlesIndexController extends Controller
+{
+    public function initialize(): void
+    {
+        parent::initialize();
+        $this->loadComponent('Paginator');
+    }
+}