-
Notifications
You must be signed in to change notification settings - Fork 70
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Separate the global site tag setup and event tracking #398
Separate the global site tag setup and event tracking #398
Conversation
It's loaded async anyway, so we don't need to hack to have it in place for the tracker definition.
0ac886d
to
5a9d161
Compare
I have quite a revolutionary proposal, to change the quite revolutionary, but TBH, at the stage of WDYT? |
includes/class-wc-google-gtag-js.php
Outdated
// phpcs:disable WordPress.WP.EnqueuedResources.NonEnqueuedScript | ||
printf( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we should be so brutal to break WP rules and print script directly; maybe enqueueing it as an inline with/ no dependencies would be enough? Or does the "optimization" plugins could force scripts enqueued in the head to be loaded later?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some of those plugins have settings to combine all inline JS and some will move them to a different file. As we know the script needs to be available at this point no matter what I think it makes sense that we avoid that possibility.
Instead of outputting it directly I've wrapped it in wp_print_inline_script_tag
so that's we're still doing things the WordPress way 582e41b
and hae a place to document the expected config data structure.
7b3ef32
to
c241828
Compare
If we aim to support "optimization" plugins, it seems there is another plugin that turns parges into static ones, which undermines the entire idea of setting any data in PHP. See #399 |
remove config file, pass settings and configs as arguments.
… from tests we no longer add it
Check that no dataLayer is set.
I wonder if we really need to go that extreme. Pre 2.0.0 implementation used I'm concerned that if we were using the WP queue before, then maybe there are extensions in the wild that rely on that. For example, there may be a need to use the WP queue system to hook their scripts before/after to add/overwrite some |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested locally with and w/o Autooptimizer, and reviewed the code. LGTM
I'd appreciate someone reviewing my code :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left some additional comments as I looked through the code.
As @tomalec mentioned, I'm also a bit concerned about the use of wp_print_inline_script_tag
compared to the previous use of wp_add_inline_script
.
There is no filter for the contents of wp_print_inline_script_tag
only for the script attributes. Whereas wp_add_inline_script
allows us a lot more flexibility. That becomes evident with the GLA compatibility where it isn't able to set config data right after the snippet in the head.
includes/class-wc-google-gtag-js.php
Outdated
esc_js( self::get( 'ga_id' ) ), | ||
esc_js( self::tracker_function_name() ), | ||
esc_js( self::DEVELOPER_ID ), | ||
json_encode( self::get_consent_modes() ), | ||
json_encode( $this->get_site_tag_config() ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we tied to legacy function names / visibility here? Just looks confusing to be mixing self
and $this
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had a bigger concern with the self::
functions, especially self::get
.
But I didn't want to push it here, not to refactor everything at once.
We use a lot of static
functions that are not 100% "static," IMO.
Declaring class properties or methods as static makes them accessible without needing an instantiation of the class.
public static function get_enabled_events
usesself::get
public static function load_opt_out
usesself::get
public static function get_product_identifier
usesself::get
public static function get
uses theself::settings
but we set theself::settings
in the instance constructor https://github.com/woocommerce/woocommerce-google-analytics-integration/blob/cdb60c08a9cb62f351f5d324472ad62b6eef7105/includes/class-wc-google-gtag-js.php#L47:L47
So we could change self::get( * )
to $this->get()
or event $this->settings[ * ]
(IMHO, get
just blurs the picture here w/o much benefit).
Do you also suggest changing tracker_function_name
, DEVELOPER_ID
, and get_consent_modes
to be instance methods/props?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest in this case to access the methods through the current instance, which will make it easier in the future to remove static
from those function declarations. We could do that now but then code snippets / other plugins would have a little harder time getting the instance.
esc_js( self::get( 'ga_id' ) ), | |
esc_js( self::tracker_function_name() ), | |
esc_js( self::DEVELOPER_ID ), | |
json_encode( self::get_consent_modes() ), | |
json_encode( $this->get_site_tag_config() ) | |
esc_js( $this->get( 'ga_id' ) ), | |
esc_js( $this->tracker_function_name() ), | |
esc_js( self::DEVELOPER_ID ), | |
json_encode( $this->get_consent_modes() ), | |
json_encode( $this->get_site_tag_config() ) |
Generally the purpose of the get
helper function would be to enforce both the parameter type for key and the return value. However it seems the return value for non_scalar values is broken. If you do something like this you'll get a fatal error:
self::$settings['ga_id'] = [];
var_dump( $this->get( 'ga_id' ) );
Where we do see it working is for example if we do something like:
self::$settings['ga_id'] = 1;
var_dump( $this->get( 'ga_id' ), self::$settings['ga_id'] );
The first value will be a string, but the second will be an int, so we will be less certain what kind of value we will be working with if we don't use the get
method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally the purpose of the get helper function would be to enforce both the parameter type for key and the return value. However it seems the return value for non_scalar values is broken. If you do something like this, you'll get a fatal error:
I wonder if we really need such behavior. I was experimenting with adding array-like settings for consent modes, and this stringification behavior was more of a problem than a help. But that's also something to address in a separate PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely limiting, and assuming of what settings are like. Agree that it would be better handled in a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added an issue to track that enhancement idea #412
includes/class-wc-google-gtag-js.php
Outdated
* @return void | ||
*/ | ||
public function setup_site_tag() { | ||
wp_print_inline_script_tag( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does printing the snippet directly have any consequences for other plugins extending the code?
These solutions are not as compatible anymore: https://github.com/woocommerce/google-listings-and-ads/blob/2.6.4/src/Google/GlobalSiteTag.php#L220-L237
In theory the filter woocommerce-google-analytics-integration
still works, but that's added much later and not right after the snippet in the head (the timing when we want to set configuration).
We can't use the filter woocommerce_ga_gtag_config
because we don't want to add additional config parameters, we want to add tracking to a second GA_ID. Maybe moving the three gtag
lines to an array of strings and then filtering the array before each line is added would be more flexible.
to more accurately reflect what they are. Address #398 (comment)
Thank you, @mikkamp, for the detailed review! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for addressing the additional comments, I've added some additional thoughts there.
At this point the only thing in this PR that I'm a little on the fence about is:
There is no filter for the contents of
wp_print_inline_script_tag
only for the script attributes. Whereaswp_add_inline_script
allows us a lot more flexibility. That becomes evident with the GLA compatibility where it isn't able to set config data right after the snippet in the head.
GLA still injects triggers for the events directly within the page body, so we'll want to ensure that the configuration is set early (in the head). One of the following options could resolve that:
- Allow filtering of the snippet added in
wp_print_inline_script_tag
(the whole snippet, or filter for adding extra lines) - Revert back to using
wp_add_inline_script
so we can enqueue configuration right after the script.
In addition to that I think if we'd pick like 3 or 4 of the most popular optimizer plugins, if we are compatible with more or less the default configurations for optimizing JS then I'd see that as acceptable to release this PR.
to avoid `Constant from class 'WC_Abstract_Google_Analytics_JS' referenced through child. PHP6606` notice
I've run some tests with a few of the popular optimisation plugins using the different configurations on offer. There have been two issues with Autoptimize, If For
|
Thanks for the nice comparison @martynmjones ! I think we can try to make workarounds to make our code work if the scripts are aggregated, loaded in random order, or extracted to external JS. They question is: do we want to? Make JS work even if data is loaded from an external We could assume it is. But if it's not it may silently corrupt the behavior, which will be hard to identify by HEs. Alternatively, we could make JS fully static, defer, and async by default, and put the data into HTML, like: Then:
Plus, we'll have the good old separation of concerns: JS is for behavior, HTML is for the content. |
warn if still not available after the document is fully loaded. Make the extension work, regardless of the order and timing of `woocommerce-google-analytics-integration` and `woocommerce-google-analytics-integration-data`scripts. Addresses #398 (comment) Make it work with Autoptimize "Aggregate inline JS" option, address #398 (comment)
@martynmjones @mikkamp I made a change in our logic flow and conditions: dacaabc
I believe this way, we're more bulletproof towards other extensions messing up with our order and timing. Pros:
Cons:
|
Thanks @martynmjones for all the testing, that's a great result, and I think sufficient enough to move forward. @tomalec I like your last addition and I think that makes it a bit more robust.
Having the cart specific data here is not the most ideal solution. Instead we should be fetching it from the cart fragments or the data used to populate the mini cart, but that's getting a little out of scope and goes beyond what we had pre 2.0 As for the other cons you listed, I think it's good to be aware of them, but not necessarily something we want to protect against. Placing some expectations in the documentation would probably be a better way to go about it for now. So from my perspective this PR is pretty much ready to go. I'm just not sure what the final conclusion to this comment was: #398 (comment) |
Thanks for all the input @mikkamp 👍 I updated the PR so it's closer to how we were previously doing it 77067d8 |
Changes proposed in this Pull Request:
The changes to how the tracker is initiated in version
2.0.0
has caused problems with more sites than anticipated. Primarily those problems come from plugins that minify and combine JS files as many of them disregard listed dependencies while doing so.As it currently stands there are many optimisation plugins that do work well with Google Analytics for WooCommerce but to make the extension more resilient against unexpected scenarios from different optimisation techniques this PR restructures how things execute.
dataLayer
andgtag
(or a function using the filtered tracker var name) are no longer included in the JS file. Instead they are added directly to the page<head>
wp_head
early so that the script is added as soon as possible. This makes it accessible to other plugins and means we are able to rely on it being available on the page regardless of when our scripts load 582e41bDOMContentLoaded
event to ensure inline script data is available at the point we begin tracking eventsDetailed test instructions:
update/separate-site-tag-setup-and-event-tracking
gtag
is still setup in the page<head>
Additional details:
Changelog entry