aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Alexandre Alapetite <alexandre@alapetite.fr> 2023-05-11 12:53:32 +0200
committerGravatar GitHub <noreply@github.com> 2023-05-11 12:53:32 +0200
commitfe7d9bbcd68660a59b813346c236b61b25a51c80 (patch)
tree95be837c2838659a3ea72974c9f9b18b1ee634ca
parent2343f0ded14ac6970575c23e4de34e536241b623 (diff)
Typed view model classes (#5380)
* Typed view model classes * Add ability to provide a typed view model class to a controller * Use `::class` instead of string for referring to classes * Examplified with `stats` and `javascript` controllers / views (more to do) * Also useful for extensions (my usecase today), which did not have the ability to define own view model attributes before. * Typo
-rw-r--r--app/Controllers/javascriptController.php7
-rw-r--r--app/Controllers/statsController.php7
-rw-r--r--app/FreshRSS.php2
-rw-r--r--app/Models/View.php44
-rw-r--r--app/Models/ViewJavascript.php16
-rw-r--r--app/Models/ViewStats.php55
-rw-r--r--app/views/javascript/actualize.phtml2
-rw-r--r--app/views/javascript/nbUnreadsPerFeed.phtml3
-rw-r--r--app/views/javascript/nonce.phtml2
-rw-r--r--app/views/stats/idle.phtml2
-rw-r--r--app/views/stats/index.phtml2
-rw-r--r--app/views/stats/repartition.phtml2
-rw-r--r--lib/Minz/ActionController.php21
-rw-r--r--tests/app/Models/UserQueryTest.php20
14 files changed, 119 insertions, 66 deletions
diff --git a/app/Controllers/javascriptController.php b/app/Controllers/javascriptController.php
index ce25d03c9..d3f73b2f9 100644
--- a/app/Controllers/javascriptController.php
+++ b/app/Controllers/javascriptController.php
@@ -2,6 +2,13 @@
class FreshRSS_javascript_Controller extends FreshRSS_ActionController {
+ /** @var FreshRSS_ViewJavascript */
+ protected $view;
+
+ public function __construct() {
+ parent::__construct(FreshRSS_ViewJavascript::class);
+ }
+
public function firstAction(): void {
$this->view->_layout(null);
}
diff --git a/app/Controllers/statsController.php b/app/Controllers/statsController.php
index 79facbbf6..d9c4fe5f4 100644
--- a/app/Controllers/statsController.php
+++ b/app/Controllers/statsController.php
@@ -5,6 +5,13 @@
*/
class FreshRSS_stats_Controller extends FreshRSS_ActionController {
+ /** @var FreshRSS_ViewStats */
+ protected $view;
+
+ public function __construct() {
+ parent::__construct(FreshRSS_ViewStats::class);
+ }
+
/**
* This action is called before every other action in that class. It is
* the common boilerplate for every action. It is triggered by the
diff --git a/app/FreshRSS.php b/app/FreshRSS.php
index 2e7ef6faa..12390b626 100644
--- a/app/FreshRSS.php
+++ b/app/FreshRSS.php
@@ -23,7 +23,7 @@ class FreshRSS extends Minz_FrontController {
Minz_Session::init('FreshRSS');
}
- Minz_ActionController::$viewType = 'FreshRSS_View';
+ Minz_ActionController::$defaultViewType = FreshRSS_View::class;
FreshRSS_Context::initSystem();
if (FreshRSS_Context::$system_conf == null) {
diff --git a/app/Models/View.php b/app/Models/View.php
index 3c56afa0e..cfa06da62 100644
--- a/app/Models/View.php
+++ b/app/Models/View.php
@@ -109,10 +109,6 @@ class FreshRSS_View extends Minz_View {
// Form login
/** @var int */
public $cookie_days;
- /** @var string */
- public $nonce;
- /** @var string */
- public $salt1;
// Registration
/** @var bool */
@@ -174,44 +170,4 @@ class FreshRSS_View extends Minz_View {
/** @var array<string,string> */
public $message;
- // Statistics
- /** @var float */
- public $average;
- /** @var float */
- public $averageDayOfWeek;
- /** @var float */
- public $averageHour;
- /** @var float */
- public $averageMonth;
- /** @var array<string> */
- public $days;
- /** @var array<string,array<int,int|string>> */
- public $entryByCategory;
- /** @var array<int,int> */
- public $entryCount;
- /** @var array<string,array<int,int|string>> */
- public $feedByCategory;
- /** @var array<int, string> */
- public $hours24Labels;
- /** @var array<string,array<int,array<string,int|string>>> */
- public $idleFeeds;
- /** @var array<int,string> */
- public $last30DaysLabel;
- /** @var array<int,string> */
- public $last30DaysLabels;
- /** @var array<string,string> */
- public $months;
- /** @var array{'total':int,'count_unreads':int,'count_reads':int,'count_favorites':int}|false */
- public $repartition;
- /** @var array{'main_stream':array{'total':int,'count_unreads':int,'count_reads':int,'count_favorites':int}|false,'all_feeds':array{'total':int,'count_unreads':int,'count_reads':int,'count_favorites':int}|false} */
- public $repartitions;
- /** @var array<int,int> */
- public $repartitionDayOfWeek;
- /** @var array<string,int>|array<int,int> */
- public $repartitionHour;
- /** @var array<int,int> */
- public $repartitionMonth;
- /** @var array<array{'id':int,'name':string,'category':string,'count':int}> */
- public $topFeed;
-
}
diff --git a/app/Models/ViewJavascript.php b/app/Models/ViewJavascript.php
new file mode 100644
index 000000000..f14810572
--- /dev/null
+++ b/app/Models/ViewJavascript.php
@@ -0,0 +1,16 @@
+<?php
+
+final class FreshRSS_ViewJavascript extends FreshRSS_View {
+
+ /** @var array<FreshRSS_Category> */
+ public $categories;
+ /** @var array<FreshRSS_Feed> */
+ public $feeds;
+ /** @var array<FreshRSS_Tag> */
+ public $tags;
+
+ /** @var string */
+ public $nonce;
+ /** @var string */
+ public $salt1;
+}
diff --git a/app/Models/ViewStats.php b/app/Models/ViewStats.php
new file mode 100644
index 000000000..03e0bc00d
--- /dev/null
+++ b/app/Models/ViewStats.php
@@ -0,0 +1,55 @@
+<?php
+
+final class FreshRSS_ViewStats extends FreshRSS_View {
+
+ /** @var FreshRSS_Category|null */
+ public $default_category;
+ /** @var array<FreshRSS_Category> */
+ public $categories;
+ /** @var FreshRSS_Feed|null */
+ public $feed;
+ /** @var array<FreshRSS_Feed> */
+ public $feeds;
+ /** @var bool */
+ public $displaySlider;
+
+ /** @var float */
+ public $average;
+ /** @var float */
+ public $averageDayOfWeek;
+ /** @var float */
+ public $averageHour;
+ /** @var float */
+ public $averageMonth;
+ /** @var array<string> */
+ public $days;
+ /** @var array<string,array<int,int|string>> */
+ public $entryByCategory;
+ /** @var array<int,int> */
+ public $entryCount;
+ /** @var array<string,array<int,int|string>> */
+ public $feedByCategory;
+ /** @var array<int, string> */
+ public $hours24Labels;
+ /** @var array<string,array<int,array<string,int|string>>> */
+ public $idleFeeds;
+ /** @var array<int,string> */
+ public $last30DaysLabel;
+ /** @var array<int,string> */
+ public $last30DaysLabels;
+ /** @var array<string,string> */
+ public $months;
+ /** @var array{'total':int,'count_unreads':int,'count_reads':int,'count_favorites':int}|false */
+ public $repartition;
+ /** @var array{'main_stream':array{'total':int,'count_unreads':int,'count_reads':int,'count_favorites':int}|false,'all_feeds':array{'total':int,'count_unreads':int,'count_reads':int,'count_favorites':int}|false} */
+ public $repartitions;
+ /** @var array<int,int> */
+ public $repartitionDayOfWeek;
+ /** @var array<string,int>|array<int,int> */
+ public $repartitionHour;
+ /** @var array<int,int> */
+ public $repartitionMonth;
+ /** @var array<array{'id':int,'name':string,'category':string,'count':int}> */
+ public $topFeed;
+
+}
diff --git a/app/views/javascript/actualize.phtml b/app/views/javascript/actualize.phtml
index 73890dd40..1a8af6e49 100644
--- a/app/views/javascript/actualize.phtml
+++ b/app/views/javascript/actualize.phtml
@@ -1,5 +1,5 @@
<?php
-/** @var FreshRSS_View $this */
+/** @var FreshRSS_ViewJavascript $this */
$categories = [];
foreach ($this->categories as $category) {
diff --git a/app/views/javascript/nbUnreadsPerFeed.phtml b/app/views/javascript/nbUnreadsPerFeed.phtml
index 261f2a179..4864b429a 100644
--- a/app/views/javascript/nbUnreadsPerFeed.phtml
+++ b/app/views/javascript/nbUnreadsPerFeed.phtml
@@ -1,5 +1,6 @@
-<?php /** @var FreshRSS_View $this */ ?>
<?php
+/** @var FreshRSS_ViewJavascript $this */
+
$result = array(
'feeds' => array(),
'tags' => array(),
diff --git a/app/views/javascript/nonce.phtml b/app/views/javascript/nonce.phtml
index f565baa85..df1389f15 100644
--- a/app/views/javascript/nonce.phtml
+++ b/app/views/javascript/nonce.phtml
@@ -1,3 +1,3 @@
-<?php /** @var FreshRSS_View $this */ ?>
<?php
+/** @var FreshRSS_ViewJavascript $this */
echo json_encode(array('salt1' => $this->salt1, 'nonce' => $this->nonce));
diff --git a/app/views/stats/idle.phtml b/app/views/stats/idle.phtml
index d94256860..37cc92144 100644
--- a/app/views/stats/idle.phtml
+++ b/app/views/stats/idle.phtml
@@ -1,5 +1,5 @@
<?php
- /** @var FreshRSS_View $this */
+ /** @var FreshRSS_ViewStats $this */
$this->partial('aside_subscription');
?>
diff --git a/app/views/stats/index.phtml b/app/views/stats/index.phtml
index a9958ee54..f4c87315f 100644
--- a/app/views/stats/index.phtml
+++ b/app/views/stats/index.phtml
@@ -1,5 +1,5 @@
<?php
- /** @var FreshRSS_View $this */
+ /** @var FreshRSS_ViewStats $this */
$this->partial('aside_subscription');
?>
diff --git a/app/views/stats/repartition.phtml b/app/views/stats/repartition.phtml
index 400cdd7fa..80e3c1a6a 100644
--- a/app/views/stats/repartition.phtml
+++ b/app/views/stats/repartition.phtml
@@ -1,5 +1,5 @@
<?php
- /** @var FreshRSS_View $this */
+ /** @var FreshRSS_ViewStats $this */
$this->partial('aside_subscription');
?>
diff --git a/lib/Minz/ActionController.php b/lib/Minz/ActionController.php
index c9551a016..553862bab 100644
--- a/lib/Minz/ActionController.php
+++ b/lib/Minz/ActionController.php
@@ -21,16 +21,27 @@ class Minz_ActionController {
protected $view;
/**
- * Gives the possibility to override the default View type.
+ * Gives the possibility to override the default view model type.
* @var class-string
+ * @deprecated Use constructor with view type instead
*/
- public static $viewType = 'Minz_View';
+ public static $defaultViewType = Minz_View::class;
- public function __construct () {
+ /**
+ * @phpstan-param class-string|'' $viewType
+ * @param string $viewType Name of the class (inheriting from Minz_View) to use for the view model
+ */
+ public function __construct(string $viewType = '') {
$this->csp_policies = self::$csp_default;
$view = null;
- if (class_exists(self::$viewType)) {
- $view = new self::$viewType();
+ if ($viewType !== '' && class_exists($viewType)) {
+ $view = new $viewType();
+ if (!($view instanceof Minz_View)) {
+ $view = null;
+ }
+ }
+ if ($view === null && class_exists(self::$defaultViewType)) {
+ $view = new self::$defaultViewType();
if (!($view instanceof Minz_View)) {
$view = null;
}
diff --git a/tests/app/Models/UserQueryTest.php b/tests/app/Models/UserQueryTest.php
index d8610a81b..c227024a9 100644
--- a/tests/app/Models/UserQueryTest.php
+++ b/tests/app/Models/UserQueryTest.php
@@ -30,13 +30,13 @@ class UserQueryTest extends PHPUnit\Framework\TestCase {
public function test__construct_whenCategoryQuery_storesCategoryParameters(): void {
$category_name = 'some category name';
/** @var FreshRSS_Category&PHPUnit\Framework\MockObject\MockObject */
- $cat = $this->createMock('FreshRSS_Category');
+ $cat = $this->createMock(FreshRSS_Category::class);
$cat->expects($this->atLeastOnce())
->method('name')
->withAnyParameters()
->willReturn($category_name);
/** @var FreshRSS_CategoryDAO&PHPUnit\Framework\MockObject\MockObject */
- $cat_dao = $this->createMock('FreshRSS_CategoryDAO');
+ $cat_dao = $this->createMock(FreshRSS_CategoryDAO::class);
$cat_dao->expects($this->atLeastOnce())
->method('searchById')
->withAnyParameters()
@@ -58,13 +58,13 @@ class UserQueryTest extends PHPUnit\Framework\TestCase {
public function test__construct_whenFeedQuery_storesFeedParameters(): void {
$feed_name = 'some feed name';
/** @var FreshRSS_Feed&PHPUnit\Framework\MockObject\MockObject */
- $feed = $this->createMock('FreshRSS_Feed');
+ $feed = $this->createMock(FreshRSS_Feed::class);
$feed->expects($this->atLeastOnce())
->method('name')
->withAnyParameters()
->willReturn($feed_name);
/** @var FreshRSS_FeedDAO&PHPUnit\Framework\MockObject\MockObject */
- $feed_dao = $this->createMock('FreshRSS_FeedDAO');
+ $feed_dao = $this->createMock(FreshRSS_FeedDAO::class);
$feed_dao->expects($this->atLeastOnce())
->method('searchById')
->withAnyParameters()
@@ -164,9 +164,9 @@ class UserQueryTest extends PHPUnit\Framework\TestCase {
public function testIsDeprecated_whenCategoryExists_returnFalse(): void {
/** @var FreshRSS_Category&PHPUnit\Framework\MockObject\MockObject */
- $cat = $this->createMock('FreshRSS_Category');
+ $cat = $this->createMock(FreshRSS_Category::class);
/** @var FreshRSS_CategoryDAO&PHPUnit\Framework\MockObject\MockObject */
- $cat_dao = $this->createMock('FreshRSS_CategoryDAO');
+ $cat_dao = $this->createMock(FreshRSS_CategoryDAO::class);
$cat_dao->expects($this->atLeastOnce())
->method('searchById')
->withAnyParameters()
@@ -178,7 +178,7 @@ class UserQueryTest extends PHPUnit\Framework\TestCase {
public function testIsDeprecated_whenCategoryDoesNotExist_returnTrue(): void {
/** @var FreshRSS_CategoryDAO&PHPUnit\Framework\MockObject\MockObject */
- $cat_dao = $this->createMock('FreshRSS_CategoryDAO');
+ $cat_dao = $this->createMock(FreshRSS_CategoryDAO::class);
$cat_dao->expects($this->atLeastOnce())
->method('searchById')
->withAnyParameters()
@@ -190,9 +190,9 @@ class UserQueryTest extends PHPUnit\Framework\TestCase {
public function testIsDeprecated_whenFeedExists_returnFalse(): void {
/** @var FreshRSS_Feed&PHPUnit\Framework\MockObject\MockObject */
- $feed = $this->createMock('FreshRSS_Feed');
+ $feed = $this->createMock(FreshRSS_Feed::class);
/** @var FreshRSS_FeedDAO&PHPUnit\Framework\MockObject\MockObject */
- $feed_dao = $this->createMock('FreshRSS_FeedDAO');
+ $feed_dao = $this->createMock(FreshRSS_FeedDAO::class);
$feed_dao->expects($this->atLeastOnce())
->method('searchById')
->withAnyParameters()
@@ -204,7 +204,7 @@ class UserQueryTest extends PHPUnit\Framework\TestCase {
public function testIsDeprecated_whenFeedDoesNotExist_returnTrue(): void {
/** @var FreshRSS_FeedDAO&PHPUnit\Framework\MockObject\MockObject */
- $feed_dao = $this->createMock('FreshRSS_FeedDAO');
+ $feed_dao = $this->createMock(FreshRSS_FeedDAO::class);
$feed_dao->expects($this->atLeastOnce())
->method('searchById')
->withAnyParameters()