PluginProbe ʕ •ᴥ•ʔ
Independent Analytics – WordPress Analytics Plugin / 2.11.9
Independent Analytics – WordPress Analytics Plugin v2.11.9
2.15.5 2.15.4 2.15.3 2.15.2 2.15.1 2.15.0 2.14.10 trunk 1.1 1.10 1.10.1 1.11 1.12 1.13 1.14 1.15 1.16 1.17 1.17.1 1.17.2 1.17.3 1.17.4 1.18 1.18.1 1.19.0 1.19.1 1.2 1.20.0 1.21.0 1.22.0 1.22.1 1.23.0 1.23.1 1.24.0 1.24.1 1.25.0 1.25.1 1.26.0 1.27.0 1.28.0 1.28.1 1.28.2 1.28.3 1.29.0 1.3 1.30.0 1.30.1 1.4 1.5 1.6 1.7 1.8 1.9 2.0.0 2.0.1 2.1.4 2.1.5 2.1.6 2.10.0 2.10.1 2.10.2 2.10.3 2.10.4 2.11.0 2.11.1 2.11.10 2.11.2 2.11.3 2.11.4 2.11.5 2.11.6 2.11.7 2.11.8 2.11.9 2.12.0 2.12.1 2.12.2 2.13.1 2.13.2 2.13.5 2.13.6 2.14.0 2.14.1 2.14.2 2.14.4 2.14.6 2.14.7 2.14.8 2.14.9 2.2.0 2.2.1 2.3.1 2.3.2 2.4.2 2.4.3 2.5.0 2.5.1 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.7.1 2.7.2 2.7.3 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.8.7 2.8.8 2.8.9 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7
independent-analytics / IAWP / View.php
independent-analytics / IAWP Last commit date
AJAX 1 year ago Admin_Page 1 year ago Click_Tracking 1 year ago Custom_WordPress_Columns 1 year ago Data_Pruning 1 year ago Date_Picker 1 year ago Date_Range 1 year ago Ecommerce 1 year ago Email_Reports 1 year ago Filter_Lists 1 year ago Form_Submissions 1 year ago Integrations 1 year ago Interval 1 year ago Menu_Bar_Stats 1 year ago Migrations 1 year ago Models 1 year ago Overview 1 year ago Public_API 1 year ago Rows 1 year ago Statistics 1 year ago Tables 1 year ago Utils 1 year ago Appearance.php 1 year ago Campaign_Builder.php 1 year ago Capability_Manager.php 1 year ago Chart.php 1 year ago Chart_Data.php 1 year ago Click_Tracking.php 1 year ago Cron_Job.php 1 year ago Cron_Manager.php 1 year ago Current_Traffic_Finder.php 1 year ago Dashboard_Options.php 1 year ago Dashboard_Widget.php 2 years ago Database.php 1 year ago Database_Manager.php 1 year ago Empty_Report_Option.php 2 years ago Env.php 1 year ago Filters.php 1 year ago Geo_Database_Background_Job.php 1 year ago Geo_Database_Manager.php 1 year ago Geoposition.php 1 year ago Icon_Directory.php 2 years ago Icon_Directory_Factory.php 2 years ago Illuminate_Builder.php 1 year ago Independent_Analytics.php 1 year ago Interrupt.php 1 year ago Known_Referrers.php 1 year ago MainWP.php 1 year ago Map.php 1 year ago Map_Data.php 1 year ago Migration_Fixer_Job.php 1 year ago Patch.php 1 year ago Payload_Validator.php 1 year ago Plugin_Conflict_Detector.php 1 year ago Plugin_Group.php 1 year ago Plugin_Group_Option.php 2 years ago Query.php 1 year ago Query_Taps.php 1 year ago Quick_Stats.php 1 year ago REST_API.php 1 year ago Real_Time.php 1 year ago Report.php 1 year ago Report_Finder.php 1 year ago Report_Options_Parser.php 2 years ago Resource_Identifier.php 1 year ago Settings.php 1 year ago Sort_Configuration.php 1 year ago Tables.php 1 year ago Track_Resource_Changes.php 1 year ago View.php 1 year ago View_Counter.php 1 year ago Views_Over_Time_Finder.php 1 year ago WP_Option_Cache_Bust.php 2 years ago
View.php
361 lines
1 <?php
2
3 namespace IAWP;
4
5 use IAWP\Custom_WordPress_Columns\Views_Column;
6 use IAWP\Models\Page;
7 use IAWP\Models\Page_Home;
8 use IAWP\Models\Page_Post_Type_Archive;
9 use IAWP\Models\Page_Singular;
10 use IAWP\Models\Visitor;
11 use IAWP\Utils\Device;
12 use IAWP\Utils\String_Util;
13 use IAWP\Utils\URL;
14 use IAWPSCOPED\Illuminate\Database\Query\JoinClause;
15 /** @internal */
16 class View
17 {
18 private $payload;
19 private $referrer_url;
20 private $visitor;
21 private $campaign_fields;
22 private $viewed_at;
23 private $resource;
24 private $session;
25 /**
26 * @param array $payload
27 * @param string|null $referrer_url
28 * @param Visitor $visitor
29 * @param array $campaign_fields
30 * @param \DateTime|null $viewed_at
31 */
32 public function __construct(array $payload, ?string $referrer_url, Visitor $visitor, array $campaign_fields, ?\DateTime $viewed_at = null)
33 {
34 $this->payload = $payload;
35 $this->referrer_url = \is_null($referrer_url) ? '' : \trim($referrer_url);
36 $this->visitor = $visitor;
37 $this->campaign_fields = $campaign_fields;
38 $this->viewed_at = $viewed_at instanceof \DateTime ? $viewed_at : new \DateTime();
39 $this->resource = $this->fetch_or_create_resource();
40 // If a resource can't be found or created, a view cannot be recorded
41 if (\is_null($this->resource)) {
42 return;
43 }
44 $this->session = $this->fetch_or_create_session();
45 $view_id = $this->create_view();
46 $this->link_with_previous_view($view_id);
47 $this->set_session_total_views();
48 $this->set_sessions_initial_view($view_id);
49 $this->set_sessions_final_view($view_id);
50 $this->set_views_postmeta($this->resource);
51 }
52 /**
53 * @return int ID of newly created session
54 */
55 public function create_session() : int
56 {
57 $sessions_table = \IAWP\Query::get_table_name(\IAWP\Query::SESSIONS);
58 return \IAWP\Illuminate_Builder::new()->from($sessions_table)->insertGetId(['visitor_id' => $this->visitor->id(), 'referrer_id' => $this->fetch_or_create_referrer(), 'country_id' => $this->fetch_or_create_country(), 'city_id' => $this->fetch_or_create_city(), 'campaign_id' => $this->get_campaign(), 'device_type_id' => Device::getInstance()->type_id(), 'device_os_id' => Device::getInstance()->os_id(), 'device_browser_id' => Device::getInstance()->browser_id(), 'is_first_session' => $this->visitor->is_first_session(), 'created_at' => $this->viewed_at()]);
59 }
60 public function fetch_or_create_country() : ?int
61 {
62 if (!$this->visitor->geoposition()->valid_location()) {
63 return null;
64 }
65 $countries_table = \IAWP\Query::get_table_name(\IAWP\Query::COUNTRIES);
66 $country_id = \IAWP\Illuminate_Builder::new()->from($countries_table)->where('country_code', '=', $this->visitor->geoposition()->country_code())->where('country', '=', $this->visitor->geoposition()->country())->where('continent', '=', $this->visitor->geoposition()->continent())->value('country_id');
67 if (!\is_null($country_id)) {
68 return $country_id;
69 }
70 \IAWP\Illuminate_Builder::new()->from($countries_table)->insertOrIgnore(['country_code' => $this->visitor->geoposition()->country_code(), 'country' => $this->visitor->geoposition()->country(), 'continent' => $this->visitor->geoposition()->continent()]);
71 return \IAWP\Illuminate_Builder::new()->from($countries_table)->where('country_code', '=', $this->visitor->geoposition()->country_code())->where('country', '=', $this->visitor->geoposition()->country())->where('continent', '=', $this->visitor->geoposition()->continent())->value('country_id');
72 }
73 public function fetch_or_create_city() : ?int
74 {
75 if (!$this->visitor->geoposition()->valid_location()) {
76 return null;
77 }
78 $country_id = $this->fetch_or_create_country();
79 $cities_table = \IAWP\Query::get_table_name(\IAWP\Query::CITIES);
80 $city_id = \IAWP\Illuminate_Builder::new()->from($cities_table)->where('country_id', $country_id)->where('subdivision', '=', $this->visitor->geoposition()->subdivision())->where('city', '=', $this->visitor->geoposition()->city())->value('city_id');
81 if (!\is_null($city_id)) {
82 return $city_id;
83 }
84 \IAWP\Illuminate_Builder::new()->from($cities_table)->insertOrIgnore(['country_id' => $country_id, 'subdivision' => $this->visitor->geoposition()->subdivision(), 'city' => $this->visitor->geoposition()->city()]);
85 return \IAWP\Illuminate_Builder::new()->from($cities_table)->where('country_id', $country_id)->where('subdivision', '=', $this->visitor->geoposition()->subdivision())->where('city', '=', $this->visitor->geoposition()->city())->value('city_id');
86 }
87 /**
88 * Fetch the last view, if any.
89 *
90 * @return int|null
91 */
92 private function fetch_last_viewed_resource() : ?int
93 {
94 global $wpdb;
95 $views_table = \IAWP\Query::get_table_name(\IAWP\Query::VIEWS);
96 $session = $this->fetch_current_session();
97 if (\is_null($session)) {
98 return null;
99 }
100 $view = $wpdb->get_row($wpdb->prepare("\n SELECT * FROM {$views_table} WHERE session_id = %d ORDER BY viewed_at DESC LIMIT 1\n ", $session->session_id));
101 if (\is_null($view)) {
102 return null;
103 }
104 return $view->resource_id;
105 }
106 private function viewed_at() : string
107 {
108 return $this->viewed_at->format('Y-m-d\\TH:i:s');
109 }
110 private function link_with_previous_view($view_id) : void
111 {
112 global $wpdb;
113 $views_tables = \IAWP\Query::get_table_name(\IAWP\Query::VIEWS);
114 $sessions_tables = \IAWP\Query::get_table_name(\IAWP\Query::SESSIONS);
115 $session = \IAWP\Illuminate_Builder::new()->from($sessions_tables)->where('session_id', '=', $this->session)->first();
116 if (\is_null($session)) {
117 return;
118 }
119 $final_view_id = $session->final_view_id;
120 $initial_view_id = $session->initial_view_id;
121 if (!\is_null($final_view_id)) {
122 $wpdb->update($views_tables, ['next_view_id' => $view_id, 'next_viewed_at' => $this->viewed_at()], ['id' => $final_view_id]);
123 } elseif (!\is_null($initial_view_id)) {
124 $wpdb->update($views_tables, ['next_view_id' => $view_id, 'next_viewed_at' => $this->viewed_at()], ['id' => $initial_view_id]);
125 }
126 }
127 private function set_session_total_views()
128 {
129 global $wpdb;
130 $sessions_table = \IAWP\Query::get_table_name(\IAWP\Query::SESSIONS);
131 $views_table = \IAWP\Query::get_table_name(\IAWP\Query::VIEWS);
132 $wpdb->query($wpdb->prepare("\n UPDATE {$sessions_table} AS sessions\n LEFT JOIN (\n SELECT\n session_id,\n COUNT(*) AS view_count\n FROM\n {$views_table} AS views\n WHERE\n views.session_id = %d\n GROUP BY\n session_id) AS view_counts ON sessions.session_id = view_counts.session_id\n SET\n sessions.total_views = COALESCE(view_counts.view_count, 0)\n WHERE sessions.session_id = %d\n ", $this->session, $this->session));
133 }
134 private function set_sessions_initial_view(int $view_id)
135 {
136 global $wpdb;
137 $sessions_table = \IAWP\Query::get_table_name(\IAWP\Query::SESSIONS);
138 $wpdb->query($wpdb->prepare("UPDATE {$sessions_table} SET initial_view_id = %d WHERE session_id = %d AND initial_view_id IS NULL", $view_id, $this->session));
139 }
140 private function set_sessions_final_view(int $view_id)
141 {
142 global $wpdb;
143 $sessions_table = \IAWP\Query::get_table_name(\IAWP\Query::SESSIONS);
144 $wpdb->query($wpdb->prepare("\n UPDATE {$sessions_table} AS sessions\n SET\n sessions.final_view_id = %d,\n sessions.ended_at = %s\n WHERE sessions.session_id = %d AND sessions.initial_view_id IS NOT NULL AND sessions.initial_view_id != %d\n ", $view_id, $this->viewed_at(), $this->session, $view_id));
145 }
146 private function create_view() : int
147 {
148 $views_table = \IAWP\Query::get_table_name(\IAWP\Query::VIEWS);
149 return \IAWP\Illuminate_Builder::new()->from($views_table)->insertGetId(['resource_id' => $this->resource->id(), 'viewed_at' => $this->viewed_at(), 'page' => $this->payload['page'], 'session_id' => $this->session]);
150 }
151 private function fetch_resource()
152 {
153 global $wpdb;
154 $resources_table = \IAWP\Query::get_table_name(\IAWP\Query::RESOURCES);
155 $query = '';
156 $payload_copy = \array_merge($this->payload);
157 unset($payload_copy['page']);
158 switch ($payload_copy['resource']) {
159 case 'singular':
160 $query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND singular_id = %d", $payload_copy['resource'], $payload_copy['singular_id']);
161 break;
162 case 'author_archive':
163 $query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND author_id = %d", $payload_copy['resource'], $payload_copy['author_id']);
164 break;
165 case 'date_archive':
166 $query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND date_archive = %s", $payload_copy['resource'], $payload_copy['date_archive']);
167 break;
168 case 'post_type_archive':
169 $query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND post_type = %s", $payload_copy['resource'], $payload_copy['post_type']);
170 break;
171 case 'term_archive':
172 $query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND term_id = %s", $payload_copy['resource'], $payload_copy['term_id']);
173 break;
174 case 'search':
175 $query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND search_query = %s", $payload_copy['resource'], $payload_copy['search_query']);
176 break;
177 case 'home':
178 $query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s ", $payload_copy['resource']);
179 break;
180 case '404':
181 $query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND not_found_url = %s", $payload_copy['resource'], $payload_copy['not_found_url']);
182 break;
183 case 'virtual_page':
184 $query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND virtual_page_id = %s", $payload_copy['resource'], $payload_copy['virtual_page_id']);
185 break;
186 }
187 $resource = $wpdb->get_row($query);
188 if (\is_null($resource)) {
189 return null;
190 }
191 return $resource;
192 }
193 private function fetch_or_create_resource() : ?Page
194 {
195 global $wpdb;
196 $resources_table = \IAWP\Query::get_table_name(\IAWP\Query::RESOURCES);
197 $resource = $this->fetch_resource();
198 if (\is_null($resource)) {
199 $payload_copy = \array_merge($this->payload);
200 unset($payload_copy['page']);
201 $wpdb->insert($resources_table, $payload_copy);
202 $resource = $this->fetch_resource();
203 }
204 if (\is_null($resource)) {
205 return null;
206 }
207 $page = Page::from_row($resource);
208 $page->update_cache();
209 return $page;
210 }
211 /**
212 * @return int|null ID of the session that should be used for this view
213 */
214 private function fetch_or_create_session() : ?int
215 {
216 $session = $this->fetch_current_session();
217 if (\is_null($session)) {
218 return $this->create_session();
219 }
220 $is_same_referrer = $this->fetch_or_create_referrer() === \intval($session->referrer_id);
221 $is_same_resource = \intval($this->fetch_resource()->id) === $this->fetch_last_viewed_resource();
222 $same_as_previous_view = $is_same_referrer && $is_same_resource;
223 // The goal here is to prevent opening multiple tabs to the site from creating multiple sessions
224 if ($is_same_referrer) {
225 return $session->session_id;
226 }
227 // The goal here is to prevent a page refresh from creating another session
228 if ($this->is_internal_referrer($this->referrer_url) || $same_as_previous_view) {
229 return $session->session_id;
230 }
231 return $this->create_session();
232 }
233 /**
234 * @param string|null $referrer_url
235 *
236 * @return bool
237 */
238 private function is_internal_referrer(?string $referrer_url) : bool
239 {
240 return !empty($referrer_url) && String_Util::str_starts_with(\strtolower($referrer_url), \strtolower(\site_url()));
241 }
242 private function fetch_referrer(array $referrer) : int
243 {
244 $referrers_table = \IAWP\Query::get_table_name(\IAWP\Query::REFERRERS);
245 $id = \IAWP\Illuminate_Builder::new()->select('id')->from($referrers_table)->where('domain', '=', $referrer['domain'])->value('id');
246 if (\is_null($id)) {
247 $id = \IAWP\Illuminate_Builder::new()->from($referrers_table)->insertGetId(['domain' => $referrer['domain'], 'type' => $referrer['type'], 'referrer' => $referrer['referrer']]);
248 }
249 return $id;
250 }
251 private function fetch_or_create_referrer() : int
252 {
253 $url = new URL($this->referrer_url);
254 $domain = $url->get_domain() ?? $this->referrer_url;
255 $known_referrer = \IAWP\Known_Referrers::get_group_for($domain);
256 // Is the referrer one of a special set of predefined known referrers?
257 if ($known_referrer) {
258 return $this->fetch_referrer(['domain' => $known_referrer['domain'], 'type' => $known_referrer['type'], 'referrer' => $known_referrer['name']]);
259 }
260 // Is the referrer invalid or for the current site?
261 if (!$url->is_valid_url() || $this->is_internal_referrer($this->referrer_url)) {
262 return $this->fetch_referrer(['domain' => '', 'type' => 'Direct', 'referrer' => 'Direct']);
263 }
264 return $this->fetch_referrer(['domain' => $url->get_domain(), 'type' => 'Referrer', 'referrer' => $this->strip_www($url->get_domain())]);
265 }
266 private function strip_www(string $string) : string
267 {
268 if (\strpos($string, "www.") !== 0) {
269 return $string;
270 }
271 return \substr($string, 4);
272 }
273 private function get_campaign() : ?int
274 {
275 global $wpdb;
276 $required_fields = ['utm_source', 'utm_medium', 'utm_campaign'];
277 $valid = \true;
278 foreach ($required_fields as $field) {
279 if (!isset($this->campaign_fields[$field])) {
280 $valid = \false;
281 }
282 }
283 if (!$valid) {
284 return null;
285 }
286 $campaigns_table = \IAWP\Query::get_table_name(\IAWP\Query::CAMPAIGNS);
287 $campaign = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$campaigns_table} WHERE landing_page_title = %s AND utm_source = %s AND utm_medium = %s AND utm_campaign = %s AND (utm_term = %s OR (%d = 0 AND utm_term IS NULL)) AND (utm_content = %s OR (%d = 0 AND utm_content IS NULL))", $this->resource->title(), $this->campaign_fields['utm_source'], $this->campaign_fields['utm_medium'], $this->campaign_fields['utm_campaign'], $this->campaign_fields['utm_term'], isset($this->campaign_fields['utm_term']) ? 1 : 0, $this->campaign_fields['utm_content'], isset($this->campaign_fields['utm_content']) ? 1 : 0));
288 if (!\is_null($campaign)) {
289 return $campaign->campaign_id;
290 }
291 $wpdb->insert($campaigns_table, ['landing_page_title' => $this->resource->title(), 'utm_source' => $this->campaign_fields['utm_source'], 'utm_medium' => $this->campaign_fields['utm_medium'], 'utm_campaign' => $this->campaign_fields['utm_campaign'], 'utm_term' => $this->campaign_fields['utm_term'], 'utm_content' => $this->campaign_fields['utm_content']]);
292 $campaign = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$campaigns_table} WHERE landing_page_title = %s AND utm_source = %s AND utm_medium = %s AND utm_campaign = %s AND (utm_term = %s OR (%d = 0 AND utm_term IS NULL)) AND (utm_content = %s OR (%d = 0 AND utm_content IS NULL))", $this->resource->title(), $this->campaign_fields['utm_source'], $this->campaign_fields['utm_medium'], $this->campaign_fields['utm_campaign'], $this->campaign_fields['utm_term'], isset($this->campaign_fields['utm_term']) ? 1 : 0, $this->campaign_fields['utm_content'], isset($this->campaign_fields['utm_content']) ? 1 : 0));
293 if (!\is_null($campaign)) {
294 return $campaign->campaign_id;
295 }
296 return null;
297 }
298 private function fetch_current_session() : ?object
299 {
300 $sessions_table = \IAWP\Query::get_table_name(\IAWP\Query::SESSIONS);
301 $session = \IAWP\Illuminate_Builder::new()->from($sessions_table, 'sessions')->selectRaw('IFNULL(ended_at, created_at) AS latest_view_at')->selectRaw('sessions.*')->where('visitor_id', '=', $this->visitor->id())->havingRaw('latest_view_at > DATE_SUB(UTC_TIMESTAMP(), INTERVAL 30 MINUTE)')->orderBy('latest_view_at', 'DESC')->first();
302 return $session;
303 }
304 private function set_views_postmeta(Page $resource) : void
305 {
306 if ($resource instanceof Page_Singular) {
307 $this->set_views_postmeta_for_singular($resource);
308 } elseif ($resource instanceof Page_Home) {
309 $this->set_views_postmeta_for_home($resource);
310 } elseif ($resource instanceof Page_Post_Type_Archive && $resource->post_type() === 'product') {
311 $this->set_views_postmeta_for_shop_page($resource);
312 }
313 }
314 private function set_views_postmeta_for_singular(Page_Singular $resource) : void
315 {
316 $singular_id = $resource->get_singular_id();
317 if ($singular_id === null) {
318 return;
319 }
320 $views_table = \IAWP\Query::get_table_name(\IAWP\Query::VIEWS);
321 $resources_table = \IAWP\Query::get_table_name(\IAWP\Query::RESOURCES);
322 $total_views = \IAWP\Illuminate_Builder::new()->selectRaw('COUNT(*) AS views')->from("{$resources_table} as resources")->join("{$views_table} AS views", function (JoinClause $join) {
323 $join->on('resources.id', '=', 'views.resource_id');
324 })->where('singular_id', '=', $singular_id)->value('views');
325 \update_post_meta($singular_id, Views_Column::$meta_key, $total_views);
326 }
327 private function set_views_postmeta_for_home(Page_Home $resource) : void
328 {
329 $blog_page_id = \get_option('page_for_posts');
330 if (\is_string($blog_page_id) && \ctype_digit($blog_page_id)) {
331 $blog_page_id = \intval($blog_page_id);
332 }
333 $blog_page = \get_post($blog_page_id);
334 if ($blog_page === null) {
335 return;
336 }
337 $views_table = \IAWP\Query::get_table_name(\IAWP\Query::VIEWS);
338 $resources_table = \IAWP\Query::get_table_name(\IAWP\Query::RESOURCES);
339 $total_views = \IAWP\Illuminate_Builder::new()->selectRaw('COUNT(*) AS views')->from("{$resources_table} as resources")->join("{$views_table} AS views", function (JoinClause $join) {
340 $join->on('resources.id', '=', 'views.resource_id');
341 })->where('resource', '=', 'home')->value('views');
342 \update_post_meta($blog_page_id, Views_Column::$meta_key, $total_views);
343 }
344 private function set_views_postmeta_for_shop_page(Page_Post_Type_Archive $resource) : void
345 {
346 try {
347 $shop_id = wc_get_page_id('shop');
348 if ($shop_id === -1) {
349 return;
350 }
351 $views_table = \IAWP\Query::get_table_name(\IAWP\Query::VIEWS);
352 $resources_table = \IAWP\Query::get_table_name(\IAWP\Query::RESOURCES);
353 $total_views = \IAWP\Illuminate_Builder::new()->selectRaw('COUNT(*) AS views')->from("{$resources_table} as resources")->join("{$views_table} AS views", function (JoinClause $join) {
354 $join->on('resources.id', '=', 'views.resource_id');
355 })->where('resource', '=', 'post_type_archive')->where('post_type', '=', 'product')->value('views');
356 \update_post_meta($shop_id, Views_Column::$meta_key, $total_views);
357 } catch (\Throwable $e) {
358 }
359 }
360 }
361