From 9cec8a81ccbb7239402fe6633dbc31979272302a Mon Sep 17 00:00:00 2001
From: Yassine Doghri <yassine@doghri.fr>
Date: Tue, 20 Oct 2020 10:31:40 +0000
Subject: [PATCH] fix(cache): add locale for podcast and episode pages + clear
 some persisting cache in models

fixes #42, #61
---
 app/Controllers/Episode.php  |  11 +--
 app/Controllers/Podcast.php  |   6 +-
 app/Models/EpisodeModel.php  | 181 +++++++++++++++++++----------------
 app/Models/PageModel.php     |  37 ++++++-
 app/Models/PlatformModel.php |  34 ++++++-
 app/Models/PodcastModel.php  |  46 +++++++--
 6 files changed, 209 insertions(+), 106 deletions(-)

diff --git a/app/Controllers/Episode.php b/app/Controllers/Episode.php
index 8b3409014e..38c83da97d 100644
--- a/app/Controllers/Episode.php
+++ b/app/Controllers/Episode.php
@@ -44,11 +44,10 @@ class Episode extends BaseController
     {
         self::triggerWebpageHit($this->episode->podcast_id);
 
-        if (
-            !($cachedView = cache(
-                "page_podcast{$this->episode->podcast_id}_episode{$this->episode->id}"
-            ))
-        ) {
+        $locale = service('request')->getLocale();
+        $cacheName = "page_podcast{$this->episode->podcast_id}_episode{$this->episode->id}_{$locale}";
+
+        if (!($cachedView = cache($cacheName))) {
             $previousNextEpisodes = (new EpisodeModel())->getPreviousNextEpisodes(
                 $this->episode,
                 $this->podcast->type
@@ -64,7 +63,7 @@ class Episode extends BaseController
             // The page cache is set to a decade so it is deleted manually upon podcast update
             return view('episode', $data, [
                 'cache' => DECADE,
-                'cache_name' => "page_podcast{$this->episode->podcast_id}_episode{$this->episode->id}",
+                'cache_name' => $cacheName,
             ]);
         }
 
diff --git a/app/Controllers/Podcast.php b/app/Controllers/Podcast.php
index 27892cdb61..5d555f293e 100644
--- a/app/Controllers/Podcast.php
+++ b/app/Controllers/Podcast.php
@@ -58,15 +58,13 @@ class Podcast extends BaseController
                 "podcast{$this->podcast->id}",
                 $yearQuery,
                 $seasonQuery ? 'season' . $seasonQuery : null,
+                service('request')->getLocale(),
             ])
         );
 
         if (!($found = cache($cacheName))) {
-            // The page cache is set to a decade so it is deleted manually upon podcast update
-            // $this->cachePage(DECADE);
-            $episodeModel = new EpisodeModel();
-
             // Build navigation array
+            $episodeModel = new EpisodeModel();
             $years = $episodeModel->getYears($this->podcast->id);
             $seasons = $episodeModel->getSeasons($this->podcast->id);
 
diff --git a/app/Models/EpisodeModel.php b/app/Models/EpisodeModel.php
index 1bd2440b23..eb1e1f990d 100644
--- a/app/Models/EpisodeModel.php
+++ b/app/Models/EpisodeModel.php
@@ -76,54 +76,6 @@ class EpisodeModel extends Model
         return $data;
     }
 
-    protected function clearCache(array $data)
-    {
-        $episodeModel = new EpisodeModel();
-
-        $episode = $episodeModel->find(
-            is_array($data['id']) ? $data['id'][0] : $data['id']
-        );
-
-        // delete cache for rss feed, podcast and episode pages
-        cache()->delete(md5($episode->podcast->feed_url));
-        cache()->delete(md5($episode->podcast->link));
-        cache()->delete(md5($episode->link));
-
-        // delete model requests cache
-        cache()->delete("podcast{$episode->podcast_id}_episodes");
-
-        // delete episode lists cache per year / season
-        $years = $episodeModel->getYears($episode->podcast_id);
-        $seasons = $episodeModel->getSeasons($episode->podcast_id);
-
-        foreach ($years as $year) {
-            cache()->delete(
-                "podcast{$episode->podcast_id}_{$year['year']}_episodes"
-            );
-            cache()->delete(
-                "page_podcast{$episode->podcast_id}_{$year['year']}"
-            );
-        }
-        foreach ($seasons as $season) {
-            cache()->delete(
-                "podcast{$episode->podcast_id}_season{$season['season_number']}_episodes"
-            );
-            cache()->delete(
-                "page_podcast{$episode->podcast_id}_season{$season['season_number']}"
-            );
-        }
-
-        cache()->delete("podcast{$episode->podcast_id}_defaultQuery");
-        cache()->delete("podcast{$episode->podcast_id}_years");
-        cache()->delete("podcast{$episode->podcast_id}_seasons");
-
-        cache()->delete(
-            "podcast{$episode->podcast_id}_episode@{$episode->slug}"
-        );
-
-        return $data;
-    }
-
     public function getEpisodeBySlug($podcastId, $episodeSlug)
     {
         if (!($found = cache("podcast{$podcastId}_episode@{$episodeSlug}"))) {
@@ -142,6 +94,47 @@ class EpisodeModel extends Model
         return $found;
     }
 
+    /**
+     * Returns the previous episode based on episode ordering
+     */
+    public function getPreviousNextEpisodes($episode, $podcastType)
+    {
+        $sortNumberField =
+            $podcastType == 'serial'
+                ? 'if(isnull(season_number),0,season_number)*1000+number'
+                : 'if(isnull(season_number),0,season_number)*100000000000000+published_at';
+        $sortNumberValue =
+            $podcastType == 'serial'
+                ? (empty($episode->season_number)
+                        ? 0
+                        : $episode->season_number) *
+                        1000 +
+                    $episode->number
+                : (empty($episode->season_number)
+                        ? ''
+                        : $episode->season_number) .
+                    date('YmdHis', strtotime($episode->published_at));
+
+        $previousData = $this->orderBy('(' . $sortNumberField . ') DESC')
+            ->where([
+                'podcast_id' => $episode->podcast_id,
+                $sortNumberField . ' <' => $sortNumberValue,
+            ])
+            ->first();
+
+        $nextData = $this->orderBy('(' . $sortNumberField . ') ASC')
+            ->where([
+                'podcast_id' => $episode->podcast_id,
+                $sortNumberField . ' >' => $sortNumberValue,
+            ])
+            ->first();
+
+        return [
+            'previous' => $previousData,
+            'next' => $nextData,
+        ];
+    }
+
     /**
      * Gets all episodes for a podcast ordered according to podcast type
      * Filtered depending on year or season
@@ -239,6 +232,10 @@ class EpisodeModel extends Model
 
     /**
      * Returns the default query for displaying the episode list on the podcast page
+     *
+     * @param int $podcastId
+     *
+     * @return array
      */
     public function getDefaultQuery(int $podcastId)
     {
@@ -267,44 +264,62 @@ class EpisodeModel extends Model
         return $defaultQuery;
     }
 
-    /**
-     * Returns the previous episode based on episode ordering
-     */
-    public function getPreviousNextEpisodes($episode, $podcastType)
+    protected function clearCache(array $data)
     {
-        $sortNumberField =
-            $podcastType == 'serial'
-                ? 'if(isnull(season_number),0,season_number)*1000+number'
-                : 'if(isnull(season_number),0,season_number)*100000000000000+published_at';
-        $sortNumberValue =
-            $podcastType == 'serial'
-                ? (empty($episode->season_number)
-                        ? 0
-                        : $episode->season_number) *
-                        1000 +
-                    $episode->number
-                : (empty($episode->season_number)
-                        ? ''
-                        : $episode->season_number) .
-                    date('YmdHis', strtotime($episode->published_at));
+        $episodeModel = new EpisodeModel();
+        $episode = (new EpisodeModel())->find(
+            is_array($data['id']) ? $data['id'][0] : $data['id']
+        );
 
-        $previousData = $this->orderBy('(' . $sortNumberField . ') DESC')
-            ->where([
-                'podcast_id' => $episode->podcast_id,
-                $sortNumberField . ' <' => $sortNumberValue,
-            ])
-            ->first();
+        // delete cache for rss feed
+        cache()->delete(md5($episode->podcast->feed_url));
 
-        $nextData = $this->orderBy('(' . $sortNumberField . ') ASC')
-            ->where([
-                'podcast_id' => $episode->podcast_id,
-                $sortNumberField . ' >' => $sortNumberValue,
-            ])
-            ->first();
+        // delete model requests cache
+        cache()->delete("podcast{$episode->podcast_id}_episodes");
 
-        return [
-            'previous' => $previousData,
-            'next' => $nextData,
-        ];
+        cache()->delete(
+            "podcast{$episode->podcast_id}_episode@{$episode->slug}"
+        );
+
+        // delete episode lists cache per year / season for a podcast
+        // and localized pages
+        $years = $episodeModel->getYears($episode->podcast_id);
+        $seasons = $episodeModel->getSeasons($episode->podcast_id);
+        $supportedLocales = config('App')->supportedLocales;
+
+        foreach ($supportedLocales as $locale) {
+            cache()->delete(
+                "page_podcast{$episode->podcast->id}_episode{$episode->id}_{$locale}"
+            );
+        }
+
+        foreach ($years as $year) {
+            cache()->delete(
+                "podcast{$episode->podcast_id}_{$year['year']}_episodes"
+            );
+            foreach ($supportedLocales as $locale) {
+                cache()->delete(
+                    "page_podcast{$episode->podcast_id}_{$year['year']}_{$locale}"
+                );
+            }
+        }
+
+        foreach ($seasons as $season) {
+            cache()->delete(
+                "podcast{$episode->podcast_id}_season{$season['season_number']}_episodes"
+            );
+            foreach ($supportedLocales as $locale) {
+                cache()->delete(
+                    "page_podcast{$episode->podcast_id}_season{$season['season_number']}_{$locale}"
+                );
+            }
+        }
+
+        // delete query cache
+        cache()->delete("podcast{$episode->podcast_id}_defaultQuery");
+        cache()->delete("podcast{$episode->podcast_id}_years");
+        cache()->delete("podcast{$episode->podcast_id}_seasons");
+
+        return $data;
     }
 }
diff --git a/app/Models/PageModel.php b/app/Models/PageModel.php
index 8208ebe28b..c6a5e20784 100644
--- a/app/Models/PageModel.php
+++ b/app/Models/PageModel.php
@@ -30,7 +30,8 @@ class PageModel extends Model
     ];
     protected $validationMessages = [];
 
-    // Before update because slug might change
+    // Before update because slug or title might change
+    protected $afterInsert = ['clearCache'];
     protected $beforeUpdate = ['clearCache'];
     protected $beforeDelete = ['clearCache'];
 
@@ -44,8 +45,38 @@ class PageModel extends Model
         cache()->delete(md5($page->link));
 
         // Clear the cache of all podcast and episode pages
-        // TODO: change the logic of page caching to prevent clearing all cache every time
-        // cache()->clean();
+        $allPodcasts = (new PodcastModel())->findAll();
+
+        foreach ($allPodcasts as $podcast) {
+            // delete localized podcast and episode page cache
+            $episodeModel = new EpisodeModel();
+            $years = $episodeModel->getYears($podcast->id);
+            $seasons = $episodeModel->getSeasons($podcast->id);
+            $supportedLocales = config('App')->supportedLocales;
+
+            foreach ($years as $year) {
+                foreach ($supportedLocales as $locale) {
+                    cache()->delete(
+                        "page_podcast{$podcast->id}_{$year['year']}_{$locale}"
+                    );
+                }
+            }
+            foreach ($seasons as $season) {
+                foreach ($supportedLocales as $locale) {
+                    cache()->delete(
+                        "page_podcast{$podcast->id}_season{$season['season_number']}_{$locale}"
+                    );
+                }
+            }
+
+            foreach ($podcast->episodes as $episode) {
+                foreach ($supportedLocales as $locale) {
+                    cache()->delete(
+                        "page_podcast{$podcast->id}_episode{$episode->id}_{$locale}"
+                    );
+                }
+            }
+        }
 
         return $data;
     }
diff --git a/app/Models/PlatformModel.php b/app/Models/PlatformModel.php
index cb24ad1a1e..abba0a525f 100644
--- a/app/Models/PlatformModel.php
+++ b/app/Models/PlatformModel.php
@@ -76,8 +76,7 @@ class PlatformModel extends Model
 
     public function savePlatformLinks($podcastId, $platformLinksData)
     {
-        cache()->delete("podcast{$podcastId}_platforms");
-        cache()->delete("podcast{$podcastId}_platformLinks");
+        $this->clearCache($podcastId);
 
         // Remove already previously set platforms to overwrite them
         $this->db
@@ -109,12 +108,39 @@ class PlatformModel extends Model
 
     public function removePlatformLink($podcastId, $platformId)
     {
-        cache()->delete("podcast{$podcastId}_platforms");
-        cache()->delete("podcast{$podcastId}_platformLinks");
+        $this->clearCache($podcastId);
 
         return $this->db->table('platform_links')->delete([
             'podcast_id' => $podcastId,
             'platform_id' => $platformId,
         ]);
     }
+
+    public function clearCache($podcastId)
+    {
+        cache()->delete("podcast{$podcastId}_platforms");
+        cache()->delete("podcast{$podcastId}_platformLinks");
+
+        // delete localized podcast page cache
+        $episodeModel = new EpisodeModel();
+        $years = $episodeModel->getYears($podcastId);
+        $seasons = $episodeModel->getSeasons($podcastId);
+        $supportedLocales = config('App')->supportedLocales;
+
+        foreach ($years as $year) {
+            foreach ($supportedLocales as $locale) {
+                cache()->delete(
+                    "page_podcast{$podcastId}_{$year['year']}_{$locale}"
+                );
+            }
+        }
+
+        foreach ($seasons as $season) {
+            foreach ($supportedLocales as $locale) {
+                cache()->delete(
+                    "page_podcast{$podcastId}_season{$season['season_number']}_{$locale}"
+                );
+            }
+        }
+    }
 }
diff --git a/app/Models/PodcastModel.php b/app/Models/PodcastModel.php
index 84b26e6ba7..bb92d0e926 100644
--- a/app/Models/PodcastModel.php
+++ b/app/Models/PodcastModel.php
@@ -170,20 +170,54 @@ class PodcastModel extends Model
         $podcast = (new PodcastModel())->getPodcastById(
             is_array($data['id']) ? $data['id'][0] : $data['id']
         );
+        $supportedLocales = config('App')->supportedLocales;
 
         // delete cache for rss feed and podcast pages
         cache()->delete(md5($podcast->feed_url));
-        cache()->delete(md5($podcast->link));
-
-        // clear cache for every podcast's episode page?
-        foreach ($podcast->episodes as $episode) {
-            cache()->delete(md5($episode->link));
-        }
 
         // delete model requests cache
         cache()->delete("podcast{$podcast->id}");
         cache()->delete("podcast@{$podcast->name}");
 
+        // clear cache for every localized podcast episode page
+        foreach ($podcast->episodes as $episode) {
+            foreach ($supportedLocales as $locale) {
+                cache()->delete(
+                    "page_podcast{$podcast->id}_episode{$episode->id}_{$locale}"
+                );
+            }
+        }
+
+        // delete episode lists cache per year / season
+        // and localized pages
+        $episodeModel = new EpisodeModel();
+        $years = $episodeModel->getYears($podcast->id);
+        $seasons = $episodeModel->getSeasons($podcast->id);
+
+        foreach ($years as $year) {
+            cache()->delete("podcast{$podcast->id}_{$year['year']}_episodes");
+            foreach ($supportedLocales as $locale) {
+                cache()->delete(
+                    "page_podcast{$podcast->id}_{$year['year']}_{$locale}"
+                );
+            }
+        }
+        foreach ($seasons as $season) {
+            cache()->delete(
+                "podcast{$podcast->id}_season{$season['season_number']}_episodes"
+            );
+            foreach ($supportedLocales as $locale) {
+                cache()->delete(
+                    "page_podcast{$podcast->id}_season{$season['season_number']}_{$locale}"
+                );
+            }
+        }
+
+        // delete query cache
+        cache()->delete("podcast{$podcast->id}_defaultQuery");
+        cache()->delete("podcast{$podcast->id}_years");
+        cache()->delete("podcast{$podcast->id}_seasons");
+
         return $data;
     }
 }
-- 
GitLab