Skip to content

Commit 199e89a

Browse files
authored
Merge pull request #244 from plausible/direct_links_support
Added: support for add to cart tracking through direct links
2 parents c9fb615 + 1b86992 commit 199e89a

File tree

4 files changed

+86
-14
lines changed

4 files changed

+86
-14
lines changed

src/Integrations/WooCommerce.php

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,10 @@ private function init( $init ) {
6969
*/
7070
add_action( 'woocommerce_before_add_to_cart_quantity', [ $this, 'add_cart_form_hidden_input' ] );
7171
add_action( 'woocommerce_after_add_to_cart_form', [ $this, 'track_add_to_cart_on_product_page' ] );
72-
add_filter( 'woocommerce_store_api_validate_add_to_cart', [ $this, 'track_add_to_cart' ], 10, 2 );
73-
add_filter( 'woocommerce_ajax_added_to_cart', [ $this, 'track_ajax_add_to_cart' ] );
72+
add_action( 'woocommerce_store_api_validate_add_to_cart', [ $this, 'track_add_to_cart' ], 10, 2 );
73+
add_action( 'woocommerce_ajax_added_to_cart', [ $this, 'track_ajax_add_to_cart' ] );
74+
/** @see \WC_Form_Handler::add_to_cart_action() runs on priority 20. */
75+
add_action( 'wp_loaded', [ $this, 'track_direct_add_to_cart' ], 21 );
7476
add_action( 'woocommerce_remove_cart_item', [ $this, 'track_remove_cart_item' ], 10, 2 );
7577
add_action( 'wp_head', [ $this, 'track_entered_checkout' ] );
7678
add_action( 'woocommerce_thankyou', [ $this, 'track_purchase' ] );
@@ -173,22 +175,22 @@ public function track_add_to_cart_on_product_page() {
173175
}
174176

175177
/**
176-
* Track (non-Interactivity API, i.e. AJAX) add to cart events.
177-
*
178-
* @param string|int $product_id ID of the product added to the cart.
178+
* Track add to cart actions by direct link, e.g. ?product_type=download&add-to-cart=1&quantity=1
179179
*
180180
* @return void
181181
*
182-
* @codeCoverageIgnore Because we can't test XHR requests here.
182+
* @codeCoverageIgnore Because we can't test XHR here.
183183
*/
184-
public function track_ajax_add_to_cart( $product_id ) {
185-
$product = wc_get_product( $product_id );
186-
$add_to_cart_data = [
187-
'id' => $product_id,
188-
'quantity' => $_POST[ 'quantity' ] ?? 1,
189-
];
184+
public function track_direct_add_to_cart() {
185+
if ( ! isset( $_REQUEST[ 'add-to-cart' ] ) || ! is_numeric( wp_unslash( $_REQUEST[ 'add-to-cart' ] ) ) ) {
186+
return;
187+
}
190188

191-
$this->track_add_to_cart( $product, $add_to_cart_data );
189+
$product_id = absint( wp_unslash( $_REQUEST[ 'add-to-cart' ] ) );
190+
$product = wc_get_product( $product_id );
191+
$quantity = isset( $_REQUEST[ 'quantity' ] ) ? absint( wp_unslash( $_REQUEST[ 'quantity' ] ) ) : 1;
192+
193+
$this->track_add_to_cart( $product, [ 'id' => $product_id, 'quantity' => $quantity ] );
192194
}
193195

194196
/**
@@ -241,6 +243,25 @@ private function clean_data( $product ) {
241243
return $product;
242244
}
243245

246+
/**
247+
* Track (non-Interactivity API, i.e. AJAX) add to cart events.
248+
*
249+
* @param string|int $product_id ID of the product added to the cart.
250+
*
251+
* @return void
252+
*
253+
* @codeCoverageIgnore Because we can't test XHR requests here.
254+
*/
255+
public function track_ajax_add_to_cart( $product_id ) {
256+
$product = wc_get_product( $product_id );
257+
$add_to_cart_data = [
258+
'id' => $product_id,
259+
'quantity' => $_POST[ 'quantity' ] ?? 1,
260+
];
261+
262+
$this->track_add_to_cart( $product, $add_to_cart_data );
263+
}
264+
244265
/**
245266
* Track Remove from cart events.
246267
*

src/Proxy.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,11 @@ public function do_request( $name = 'pageview', $domain = '', $url = '', $props
112112
'u' => $url ?: wp_get_referer(),
113113
];
114114

115+
// URL is required, so if no $url was set and no referer was found, attempt to create it from the REQUEST_URI server variable.
116+
if ( empty( $body[ 'u' ] ) ) {
117+
$body[ 'u' ] = $this->generate_event_url(); // @codeCoverageIgnore
118+
}
119+
115120
// Revenue events use a different approach.
116121
if ( isset( $props[ 'revenue' ] ) ) {
117122
$body[ 'revenue' ] = reset( $props ); // @codeCoverageIgnore
@@ -124,6 +129,23 @@ public function do_request( $name = 'pageview', $domain = '', $url = '', $props
124129
return $this->send_event( $request );
125130
}
126131

132+
/**
133+
* Attempts to generate the Event URL from available resources.
134+
*
135+
* @return string
136+
*/
137+
public function generate_event_url() {
138+
$url = '';
139+
$parts = parse_url( $_SERVER[ 'REQUEST_URI' ] );
140+
$home_url_parts = parse_url( get_home_url() );
141+
142+
if ( isset( $home_url_parts[ 'scheme' ] ) && isset( $home_url_parts[ 'host' ] ) && isset( $parts[ 'path' ] ) ) {
143+
$url = $home_url_parts[ 'scheme' ] . '://' . $home_url_parts [ 'host' ] . $parts[ 'path' ];
144+
}
145+
146+
return $url;
147+
}
148+
127149
/**
128150
* Formats and sends $request to the Plausible API.
129151
*

tests/unit/ClientFactoryTest.php renamed to tests/integration/ClientFactoryTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* @package Plausible Analytics Unit Tests - ClientFactory
44
*/
55

6-
namespace Plausible\Analytics\Tests\Unit;
6+
namespace Plausible\Analytics\Tests\Integration;
77

88
use Plausible\Analytics\Tests\TestCase;
99
use Plausible\Analytics\WP\Client;

tests/integration/ProxyTest.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
/**
3+
* @package Plausible Analytics Unit Tests - ClientFactory
4+
*/
5+
6+
namespace Plausible\Analytics\Tests\Integration;
7+
8+
use Plausible\Analytics\Tests\TestCase;
9+
use Plausible\Analytics\WP\Proxy;
10+
11+
class ProxyTest extends TestCase {
12+
/**
13+
* @see Proxy::generate_event_url()
14+
* @return void
15+
*/
16+
public function testGenerateEventUrl() {
17+
$proxy = new Proxy( false );
18+
19+
$url = $proxy->generate_event_url();
20+
21+
$this->assertEquals( 'http://example.org', $url );
22+
23+
$_SERVER[ 'REQUEST_URI' ] = '/test';
24+
25+
$url = $proxy->generate_event_url();
26+
27+
$this->assertEquals( 'http://example.org/test', $url );
28+
}
29+
}

0 commit comments

Comments
 (0)