Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
WPCom_Themes_Service
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 9
210
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 filter_themes_api_result_recommended
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 filter_themes_api_result_search
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 map_and_filter_wpcom_themes
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 has_valid_theme_tier
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 get_theme
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 filter_themes_api_result_latest
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 filter_themes_api_result_block_themes
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 filter_themes_api_result_feature_filter
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * Class WPCom_Themes_Service.
4 * Component that interacts with the WordPress.com themes API and has the business logic to filter the partner themes.
5 *
6 * @package wpcom-themes
7 */
8
9/**
10 * WordPress.com themes service.
11 */
12class WPCom_Themes_Service {
13    /**
14     * Mapper to map WPCom themes to WPORG themes.
15     *
16     * @var WPCom_Themes_Mapper
17     */
18    private WPCom_Themes_Mapper $mapper;
19
20    /**
21     * WordPress.com themes API.
22     *
23     * @var WPCom_Themes_Api
24     */
25    private WPCom_Themes_Api $api;
26
27    /**
28     * Theme merge strategy collection.
29     *
30     * @var WPCom_Themes_Merger
31     */
32    private WPCom_Themes_Merger $merger;
33
34    /**
35     * Class constructor.
36     *
37     * @param WPCom_Themes_Api    $api    The WordPress.com themes API.
38     * @param WPCom_Themes_Mapper $mapper The mapper to map WPCom themes to WPORG themes.
39     * @param WPCom_Themes_Merger $merger The theme merge strategy collection.
40     */
41    public function __construct(
42        WPCom_Themes_Api $api,
43        WPCom_Themes_Mapper $mapper,
44        WPCom_Themes_Merger $merger
45    ) {
46        $this->api    = $api;
47        $this->mapper = $mapper;
48        $this->merger = $merger;
49    }
50
51    /**
52     * Filter the themes API result to include recommended WordPress.com themes.
53     *
54     * @param stdClass $wporg_theme_api_response The WP.org themes API result.
55     *
56     * @return stdClass The themes API result including wpcom themes.
57     */
58    public function filter_themes_api_result_recommended( stdClass $wporg_theme_api_response ): stdClass {
59        $wpcom_recommended_themes = $this->api->fetch_recommended_themes();
60        $wpcom_recommended_themes = $this->map_and_filter_wpcom_themes( $wpcom_recommended_themes );
61
62        return $this->merger->merge_by_wpcom_first( $wporg_theme_api_response, $wpcom_recommended_themes );
63    }
64
65    /**
66     * Filter the themes API result to include WordPress.com themes matching a search term.
67     *
68     * @param stdClass $wporg_theme_api_response The WP.org themes API result.
69     * @param string   $search                   Search term.
70     *
71     * @return stdClass The themes API result including wpcom themes.
72     */
73    public function filter_themes_api_result_search( stdClass $wporg_theme_api_response, string $search ): stdClass {
74        $wpcom_search_themes = $this->api->search_themes( $search );
75        $wpcom_search_themes = $this->map_and_filter_wpcom_themes( $wpcom_search_themes );
76
77        return $this->merger->merge_by_wpcom_first( $wporg_theme_api_response, $wpcom_search_themes );
78    }
79
80    /**
81     * Get the WordPress.com themes that can be installed on Atomic sites without a subscription.
82     *
83     * @param array $wpcom_themes WPCom themes.
84     *
85     * @return array Filtered WPCom themes.
86     */
87    protected function map_and_filter_wpcom_themes( array $wpcom_themes ): array {
88        $themes = array();
89        foreach ( $wpcom_themes as $theme ) {
90            if ( $this->has_valid_theme_tier( $theme ) ) {
91                $themes[] = $this->mapper->map_wpcom_to_wporg( $theme );
92            }
93        }
94
95        return $themes;
96    }
97
98    /**
99     * Checks if a WPCom theme has a valid tier from Atomic sites.
100     *
101     * @param stdClass $theme The theme to check.
102     *
103     * @return bool True if the theme has a valid tier, false otherwise.
104     */
105    protected function has_valid_theme_tier( stdClass $theme ): bool {
106        $tier               = $theme->theme_tier->slug ?? 'premium';
107        $theme_tier_feature = $tier . '-themes';
108
109        return $tier !== 'partner' && ( $tier === 'free' || wpcom_site_has_feature( $theme_tier_feature ) );
110    }
111
112    /**
113     * Retrieves a WPCom theme by its slug.
114     *
115     * @param string $slug The theme slug.
116     *
117     * @return stdClass|null The theme object if found, null otherwise.
118     */
119    public function get_theme( string $slug ): ?stdClass {
120        $wpcom_theme = $this->api->fetch_theme( $slug );
121
122        if ( ! $wpcom_theme ) {
123            return null;
124        }
125
126        return $this->mapper->map_wpcom_to_wporg( $wpcom_theme );
127    }
128
129    /**
130     * Returns a list of themes that include WPCom themes sorted by release date.
131     *
132     * @param stdClass $wporg_theme_api_response The WP.org themes API result.
133     *
134     * @return stdClass The themes API result including wpcom themes.
135     */
136    public function filter_themes_api_result_latest( stdClass $wporg_theme_api_response ): stdClass {
137        $wpcom_themes = $this->api->fetch_all_non_delisted_themes();
138        $wpcom_themes = $this->map_and_filter_wpcom_themes( $wpcom_themes );
139
140        return $this->merger->merge_by_release_date( $wporg_theme_api_response, $wpcom_themes );
141    }
142
143    /**
144     * Returns a list of themes that include WPCom block themes sorted by release date.
145     *
146     * @param stdClass $wporg_theme_api_response The WP.org themes API result.
147     *
148     * @return stdClass The themes API result including wpcom themes.
149     */
150    public function filter_themes_api_result_block_themes( stdClass $wporg_theme_api_response ): stdClass {
151        $wpcom_themes = $this->api->fetch_all_non_delisted_themes();
152        $wpcom_themes = array_filter(
153            $wpcom_themes,
154            fn( $theme ) => $theme->block_theme
155        );
156        $wpcom_themes = $this->map_and_filter_wpcom_themes( $wpcom_themes );
157
158        return $this->merger->merge_by_release_date( $wporg_theme_api_response, $wpcom_themes );
159    }
160
161    /**
162     * Returns a list of themes that include WPCom themes filtered by tags.
163     *
164     * @param stdClass $wporg_theme_api_response The WP.org themes API result.
165     * @param array    $tags                     The tags to filter by.
166     *
167     * @return stdClass The themes API result including wpcom themes.
168     */
169    public function filter_themes_api_result_feature_filter( stdClass $wporg_theme_api_response, array $tags ): stdClass {
170        $wpcom_themes = $this->api->fetch_all_non_delisted_themes();
171        $wpcom_themes = $this->map_and_filter_wpcom_themes( $wpcom_themes );
172
173        $wpcom_themes = array_filter(
174            $wpcom_themes,
175            fn( $theme ) => (bool) array_intersect( $tags, $theme->tags )
176        );
177
178        return $this->merger->merge_by_wpcom_first( $wporg_theme_api_response, $wpcom_themes );
179    }
180}