diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 60ecb02..281146f 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -13,21 +13,14 @@ jobs:
fail-fast: true
matrix:
os: [ubuntu-latest]
- php: [8.1, 8.0, 7.4]
- laravel: [9.*, 8.*, 7.*]
+ php: [8.1, 8.0]
+ laravel: [9.*, 8.*]
stability: [prefer-lowest, prefer-stable]
include:
- laravel: 9.*
testbench: 7.*
- laravel: 8.*
testbench: ^6.23
- - laravel: 7.*
- testbench: ^5.20
- exclude:
- - laravel: 9.*
- php: 7.4
- - laravel: 7.*
- php: 8.1
name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - ${{ matrix.os }}
diff --git a/README.md b/README.md
index 82126ad..58bceea 100644
--- a/README.md
+++ b/README.md
@@ -32,89 +32,355 @@ return [
*/
'pixel_id' => env('CONVERSIONS_API_PIXEL_ID'),
+ /**
+ * The Google Tag Manager container ID used in case you're deduplicating
+ * events through Google Tag Manager instead of Facebook Pixel directly.
+ * Should look something like "GTM-XXXXXX".
+ */
+ 'gtm_id' => env('GOOGLE_TAG_MANAGER_ID'),
+
/**
* The Conversions API comes with a nice way to test your events.
* You may use this config variable to set your test code.
*/
- 'test_code' => null,
+ 'test_code' => env('CONVERSIONS_API_TEST_CODE'),
];
```
## Conversions API
-This package allows you to set the user data and events that will be sent to the Conversions API.
+### Events
+To add events to the conversions API you may use the `addEvent`, `addEvents` or `setEvents` methods.
+Retrieving or clearing events may be done using the `getEvents` and `clearEvents` methods:
```php
use Esign\ConversionsApi\Facades\ConversionsApi;
use FacebookAds\Object\ServerSide\UserData;
use FacebookAds\Object\ServerSide\Event;
-ConversionsApi::setUserData(
- (new UserData())->setFirstName('John')->setLastName('Doe')
-);
-ConversionsApi::setEvent(
+ConversionsApi::addEvent(
(new Event())->setEventName('PageView')->setEventId('abc')
);
+
+ConversionsApi::setEvents([
+ (new Event())->setEventName('PageView')->setEventId('abc'),
+ (new Event())->setEventName('Purchase')->setEventId('xyz'),
+]);
+
+ConversionsApi::getEvents();
+ConversionsApi::clearEvents();
+```
+
+Adding events won't cause them to be sent to the Conversions API.
+To actually send the events you must call the `sendEvents` method:
+```php
+use Esign\ConversionsApi\Facades\ConversionsApi;
+
+ConversionsApi::sendEvents();
```
-To actually send the data you must call the `execute` method.
+#### Creating event classes
+To make things a bit cleaner you may extend Facebook's default event class:
+
```php
use Esign\ConversionsApi\Facades\ConversionsApi;
+use FacebookAds\Object\ServerSide\ActionSource;
+use FacebookAds\Object\ServerSide\Event;
-ConversionsApi::execute();
+class PurchaseEvent extends Event
+{
+ public static function create(): static
+ {
+ return (new static())
+ ->setActionSource(ActionSource::WEBSITE)
+ ->setEventName('Purchase')
+ ->setEventTime(time())
+ ->setEventSourceUrl(request()->fullUrl())
+ ->setEventId((string) Str::uuid())
+ ->setUserData(ConversionsApi::getUserData());
+ }
+}
+```
+```php
+ConversionsApi::addEvent(
+ PurchaseEvent::create()
+);
```
-This package also comes with a nice helper to send `PageView` events.
-By including the `@conversionsApiPageView` directive on a page, an event with the minimum required data (ip address, user agent and request url) will be sent to the Conversions API:
+### User Data
+This package also comes with a way to define default user data for the user of the current request.
+You may do so by calling the `setUserData` method, this is typically done in your `AppServiceProvider`:
```php
-@conversionsApiPageView
+use Esign\ConversionsApi\Facades\ConversionsApi;
+use Esign\ConversionsApi\Objects\DefaultUserData;
+
+ConversionsApi::setUserData(
+ DefaultUserData::create()
+ ->setEmail(auth()->user()?->email)
+);
```
-###
+You may now pass the user data along with your events:
+```php
+use Esign\ConversionsApi\Facades\ConversionsApi;
+use FacebookAds\Object\ServerSide\Event;
+
+ConversionsApi::addEvent(
+ (new Event())->setUserData(ConversionsApi::getUserData())
+);
+```
-## Facebook Pixel
-To [deduplicate browser and server events](https://developers.facebook.com/docs/marketing-api/conversions-api/deduplicate-pixel-and-server-events/) this package will automatically generate a unique event ID for every request.
-This event ID should be passed along with your Facebook Pixel.
-This package comes with a few ways to do this:
+## Event deduplication
+This package comes with a few ways to assist you in [deduplicating browser and server events](https://developers.facebook.com/docs/marketing-api/conversions-api/deduplicate-pixel-and-server-events/). This can either be done using the Facebook Pixel directly or through Google Tag Manager's data layer.
### Facebook Pixel
-In case you want to directly load the Facebook Pixel script you may use the `@conversionsApiFacebookPixelScript` directive or directly include it.
+Before attempting to send events through Facebook Pixel make sure to load the pixel script:
+```blade
+
+```
+
+This will render the following html:
+```html
+
+```
+
+This package will attempt to provide as much advanced matching data as possible by using user data from the `ConversionsApi`.
+For example when an email address is set, it will automatically be provided to the init method:
```php
-@conversionsApiFacebookPixelScript
-@include('conversions-api::facebook-pixel-script')
+ConversionsApi::setUserData(
+ (new UserData())->setEmail('john@example.com')
+);
+```
+```js
+fbq('init', 'your-configured-pixel-id', {"em": "john@example.com"});
```
-### Google Tag Manager
-A convenient dataLayer helper is included in case you want to load the Facebook Pixel through Google Tag Manager.
-By default a variable name `conversionsApiEventId` will be used:
+Now that your Pixel is correctly initialized, it's time to send some events.
+Sadly the parameters between the Conversions API and Facebook Pixel are not identical, so they must be mapped to the [correct format](https://developers.facebook.com/docs/meta-pixel/reference).
+An easy way of doing this is by extending the `FacebookAds\Object\ServerSide\Event` class and implementing the `Esign\ConversionsApi\Contracts\MapsToFacebookPixel` interface on it:
```php
-@conversionsApiDataLayer
-@include('conversions-api::data-layer')
+use Esign\ConversionsApi\Contracts\MapsToFacebookPixel;
+use Esign\ConversionsApi\Facades\ConversionsApi;
+use FacebookAds\Object\ServerSide\ActionSource;
+use FacebookAds\Object\ServerSide\Event;
+
+class PurchaseEvent extends Event implements MapsToFacebookPixel
+{
+ public static function create(): static
+ {
+ return (new static())
+ ->setActionSource(ActionSource::WEBSITE)
+ ->setEventName('Purchase')
+ ->setEventTime(time())
+ ->setEventSourceUrl(request()->fullUrl())
+ ->setEventId((string) Str::uuid())
+ ->setUserData(ConversionsApi::getUserData());
+ }
+
+ public function getFacebookPixelEventType(): string
+ {
+ return 'track';
+ }
+
+ public function getFacebookPixelEventName(): string
+ {
+ return $this->getEventName();
+ }
+
+ public function getFacebookPixelCustomData(): array
+ {
+ $customData = $this->getCustomData();
+
+ return array_filter([
+ 'currency' => $customData?->getCurrency(),
+ 'value' => $customData?->getValue(),
+ ]);
+ }
+
+ public function getFacebookPixelEventData(): array
+ {
+ return array_filter(['eventID' => $this->getEventId()]);
+ }
+}
```
-You may also pass a custom variable name:
+You may now pass any class that implements the `MapsToFacebookPixel` interface to the view component responsible for tracking Facebook Pixel events:
```php
-@conversionsApiDataLayer('yourDataLayerVariableName')
-@include('conversions-api::data-layer', ['dataLayerVariableName' => 'yourDataLayerVariableName'])
+use FacebookAds\Object\ServerSide\CustomData;
+use Illuminate\Support\Str;
+
+$event = PurchaseEvent::create()->setCustomData(
+ (new CustomData())->setCurrency('EUR')->setValue(10)
+);
+```
+
+```blade
+
+```
+
+This will render the following script tag:
+```html
+
```
-#### Configuring Google Tag Manager
-First off, you should add a new `Data Layer Variable` to your Google Tag Manager workspace.
-
+To retrieve a list of all events that implement the `MapsToFacebookPixel` interface you may call the `filterFacebookPixelEvents` method:
+```blade
+@foreach(ConversionsApi::getEvents()->filterFacebookPixelEvents() as $event)
+
+@endforeach
+```
+
+In case you want more control over what's being rendered, you may always use the anonymous component:
+```blade
+
+```
+
+### Google Tag Manager
+Before attempting to deduplicate events through GTM make sure to configure your GTM container id and include the necessary scripts:
+
+```env
+GOOGLE_TAG_MANAGER_ID=GTM-XXXXXX
+```
-Next up you should use the variable name that was passed along to the data layer view.
-
+```blade
+
+
+
+ {{-- ... --}}
+
+
+
+ {{-- ... --}}
+
+
+```
-After saving the variable you should be able to use it in your Facebook Pixel script using the double bracket syntax: `{{ Name of your variable }}`.
-
+This package comes with a view component that will map all user data from the `ConversionsApi` to dataLayer variables:
+```blade
+
+```
+For example when an email address is set, it will be automatically mapped to a dataLayer variable.
+Check the [source](src/View/Components/DataLayerUserDataVariable.php) of the view component to see a list of all possible variables.
+```php
+ConversionsApi::setUserData(
+ (new UserData())->setEmail('john@example.com')
+);
+```
+```js
+window.dataLayer.push({"conversionsApiUserEmail": "john@example.com"});
+```
-### Manually retrieving the event ID
-In case you want to use another strategy to deduplicate your events you can do so by manually retrieving the event ID:
+Now that your Pixel through GTM is correctly initialized, it's time to send some events.
+An easy way of doing this is by extending the `FacebookAds\Object\ServerSide\Event` class and implementing the `Esign\ConversionsApi\Contracts\MapsToDataLayer` interface on it:
```php
+use Esign\ConversionsApi\Contracts\MapsToDataLayer;
use Esign\ConversionsApi\Facades\ConversionsApi;
+use FacebookAds\Object\ServerSide\ActionSource;
+use FacebookAds\Object\ServerSide\Event;
+
+class PurchaseEvent extends Event implements MapsToDataLayer
+{
+ public static function create(): static
+ {
+ return (new static())
+ ->setActionSource(ActionSource::WEBSITE)
+ ->setEventName('Purchase')
+ ->setEventTime(time())
+ ->setEventSourceUrl(request()->fullUrl())
+ ->setEventId((string) Str::uuid())
+ ->setUserData(ConversionsApi::getUserData());
+ }
-ConversionsApi::getEventId();
+ public function getDataLayerArguments(): array
+ {
+ $customData = $this->getCustomData();
+
+ return [
+ 'event' => 'conversionsApiPurchase',
+ 'conversionsApiPurchaseEventId' => $this->getEventId(),
+ 'conversionsApiPurchaseCurrency' => $customData?->getCurrency(),
+ 'conversionsApiPurchaseValue' => $customData?->getValue(),
+ ];
+ }
+}
+```
+
+You may now pass any class that implements the `MapsToDataLayer` interface to the view component responsible for tracking Facebook Pixel events:
+```php
+use FacebookAds\Object\ServerSide\CustomData;
+use Illuminate\Support\Str;
+
+$event = PurchaseEvent::create()->setCustomData(
+ (new CustomData())->setCurrency('EUR')->setValue(10)
+);
```
+```blade
+
+```
+
+This will render the following script tag:
+```html
+
+```
+
+To retrieve a list of all events that implement the `MapsToDataLayer` interface you may call the `filterDataLayerEvents` method:
+```blade
+@foreach(ConversionsApi::getEvents()->filterDataLayerEvents() as $event)
+
+@endforeach
+```
+
+In case you want more control over what's being rendered, you may always use the anonymous component:
+```blade
+
+```
+
+## PageView Events
+This package ships with some helpers to track `PageView` events out of the box.
+These helpers will automatically send both Conversions API & Facebook Pixel events and provide event deduplication.
+> **Note**
+> Make sure to always include these view components after you've already looped over any other events currently defined on the ConversionsApi. Including these view components will clear any existing events.
+
+In case you're using the Facebook Pixel directly:
+```blade
+
+```
+Or by using Google Tag Manager. The data-layer variable to deduplicate events is called `conversionsApiPageViewEventId`.
+```blade
+
+```
+
+## Troubleshooting
+### PageView events are not shown as deduplicated in the test events dashboard
+Event deduplication for PageView events should be fine out of the box, since the event name and event id parameters have been provided.
+However, when serving your application locally the ip address returned by Laravel's `request()->ip()` will be `127.0.0.1`.
+This is different from the ip address sent through Facebook Pixel, causing the Conversions API and Facebook Pixel events to not be deduplicated.
+This issue should solve itself once the application will be ran in production.
+
+
+
## Testing
```bash
diff --git a/UPGRADING.md b/UPGRADING.md
new file mode 100644
index 0000000..8683fc1
--- /dev/null
+++ b/UPGRADING.md
@@ -0,0 +1,20 @@
+## From v1 to v2
+This package now supports to send multiple events at once to the Conversions API, instead of sending them one by one. While the previous version was mainly focused on sending `PageView` events, v2 now supports all kinds of events.
+
+### Method changes
+- The `setEvent` method has been replaced by `addEvent`, `addEvents` and `setEvents` methods.
+- The `getEvent` method has been removed in favor of `getEvents`.
+- The `execute` method has been replaced by `sendEvents`.
+- The `setEventByName` method has been removed, it's recommended to create [dedicated event classes](README.MD#creating-event-classes) for this.
+- The `executePageViewEvent` has been removed and is now handled in dedicated view components, see [PageView Events](README.md#pageview-events)
+- The `getEventId` and `setEventId` methods have been removed, since this package now supports multiple events, that each have their own unique event id.
+You may use Laravel's built-in `Str::uuid()` helper to create unique event id's.
+See the [docs](README.md#creating-event-classes) for an example on this.
+
+### Directive changes
+- The `@conversionsApiPageView` directive has been replaced by 2 view components, depending on your deduplication preference.
+In case you're using the Facebook Pixel directly, use ``.
+If you're sending them through Google Tag Manager, use ``.
+Note that the dataLayer variable name has been changed from `conversionsApiEventId` to `conversionsApiPageViewEventId`.
+- The `@conversionsApiFacebookPixelScript` directive has been replaced with ``.
+- The `@conversionsApiDataLayer` has been removed. Support for dataLayer events is possible through `` or ``. See the [docs](README.md#google-tag-manager) for an example on this.
\ No newline at end of file
diff --git a/composer.json b/composer.json
index 5a79a70..a9a4e30 100644
--- a/composer.json
+++ b/composer.json
@@ -17,14 +17,14 @@
}
],
"require": {
- "php": "^7.4|^8.0",
+ "php": "^8.0",
"facebook/php-business-sdk": "^12.0",
- "illuminate/http": "^7.0|^8.0|^9.0",
- "illuminate/support": "^7.0|^8.0|^9.0"
+ "illuminate/http": "^8.0|^9.0",
+ "illuminate/support": "^8.0|^9.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.3",
- "orchestra/testbench": "^5.0|^6.0|^7.0",
+ "orchestra/testbench": "^6.0|^7.0",
"phpunit/phpunit": "^9.0"
},
"autoload": {
diff --git a/config/conversions-api.php b/config/conversions-api.php
index 7ae3695..b8867e1 100644
--- a/config/conversions-api.php
+++ b/config/conversions-api.php
@@ -11,9 +11,16 @@
*/
'pixel_id' => env('CONVERSIONS_API_PIXEL_ID'),
+ /**
+ * The Google Tag Manager container ID used in case you're deduplicating
+ * events through Google Tag Manager instead of Facebook Pixel directly.
+ * Should look something like "GTM-XXXXXX".
+ */
+ 'gtm_id' => env('GOOGLE_TAG_MANAGER_ID'),
+
/**
* The Conversions API comes with a nice way to test your events.
* You may use this config variable to set your test code.
*/
- 'test_code' => null,
+ 'test_code' => env('CONVERSIONS_API_TEST_CODE'),
];
diff --git a/docs/images/gtm-step-1.png b/docs/images/gtm-step-1.png
deleted file mode 100644
index 0fdb8a7..0000000
Binary files a/docs/images/gtm-step-1.png and /dev/null differ
diff --git a/docs/images/gtm-step-2.png b/docs/images/gtm-step-2.png
deleted file mode 100644
index a3a49b0..0000000
Binary files a/docs/images/gtm-step-2.png and /dev/null differ
diff --git a/docs/images/gtm-step-3.png b/docs/images/gtm-step-3.png
deleted file mode 100644
index 83968b6..0000000
Binary files a/docs/images/gtm-step-3.png and /dev/null differ
diff --git a/resources/views/components/data-layer-variable.blade.php b/resources/views/components/data-layer-variable.blade.php
new file mode 100644
index 0000000..9f84b36
--- /dev/null
+++ b/resources/views/components/data-layer-variable.blade.php
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/resources/views/components/facebook-pixel-script.blade.php b/resources/views/components/facebook-pixel-script.blade.php
new file mode 100644
index 0000000..42f03b4
--- /dev/null
+++ b/resources/views/components/facebook-pixel-script.blade.php
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff --git a/resources/views/components/facebook-pixel-tracking-event.blade.php b/resources/views/components/facebook-pixel-tracking-event.blade.php
new file mode 100644
index 0000000..2db7195
--- /dev/null
+++ b/resources/views/components/facebook-pixel-tracking-event.blade.php
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/resources/views/components/google-tag-manager-body.blade.php b/resources/views/components/google-tag-manager-body.blade.php
new file mode 100644
index 0000000..2390f8a
--- /dev/null
+++ b/resources/views/components/google-tag-manager-body.blade.php
@@ -0,0 +1,2 @@
+
\ No newline at end of file
diff --git a/resources/views/components/google-tag-manager-head.blade.php b/resources/views/components/google-tag-manager-head.blade.php
new file mode 100644
index 0000000..a26ddf9
--- /dev/null
+++ b/resources/views/components/google-tag-manager-head.blade.php
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/resources/views/data-layer.blade.php b/resources/views/data-layer.blade.php
deleted file mode 100644
index a9a1d0a..0000000
--- a/resources/views/data-layer.blade.php
+++ /dev/null
@@ -1,4 +0,0 @@
-
\ No newline at end of file
diff --git a/resources/views/facebook-pixel-script.blade.php b/resources/views/facebook-pixel-script.blade.php
deleted file mode 100644
index 789232c..0000000
--- a/resources/views/facebook-pixel-script.blade.php
+++ /dev/null
@@ -1,10 +0,0 @@
-
\ No newline at end of file
diff --git a/src/Collections/EventCollection.php b/src/Collections/EventCollection.php
new file mode 100644
index 0000000..209a2ae
--- /dev/null
+++ b/src/Collections/EventCollection.php
@@ -0,0 +1,20 @@
+filter(fn ($event) => $event instanceof MapsToFacebookPixel);
+ }
+
+ public function filterDataLayerEvents(): static
+ {
+ return $this->filter(fn ($event) => $event instanceof MapsToDataLayer);
+ }
+}
diff --git a/src/Contracts/MapsToDataLayer.php b/src/Contracts/MapsToDataLayer.php
new file mode 100644
index 0000000..1860fed
--- /dev/null
+++ b/src/Contracts/MapsToDataLayer.php
@@ -0,0 +1,8 @@
+request = request();
- $this->userData = $this->getDefaultUserData();
- $this->eventId = (string) Str::uuid();
+ $this->events = new EventCollection();
+ $this->setUserData(DefaultUserData::create());
Api::init(null, null, config('conversions-api.access_token'), false);
}
- protected function getDefaultUserData(): UserData
- {
- return (new UserData())
- ->setClientIpAddress($this->request->ip())
- ->setClientUserAgent($this->request->userAgent());
- }
-
public function setUserData(UserData $userData): self
{
$this->userData = $userData;
@@ -44,47 +34,41 @@ public function getUserData(): UserData
return $this->userData;
}
- public function setEvent(Event $event): self
+ public function addEvent(Event $event): self
{
- $this->event = $event;
+ $this->events->push($event);
return $this;
}
- public function getEvent(): ?Event
+ public function addEvents(iterable $events): self
{
- return $this->event;
- }
+ $this->events = $this->events->merge($events);
- public function getEventId(): string
- {
- return $this->eventId;
+ return $this;
}
- public function setEventId(string $eventId): self
+ public function setEvents(iterable $events): self
{
- $this->eventId = $eventId;
+ $this->events = new EventCollection($events);
return $this;
}
- public function setEventByName(string $eventName): self
+ public function getEvents(): EventCollection
{
- $event = (new Event())
- ->setActionSource('website')
- ->setEventName($eventName)
- ->setEventId($this->getEventId())
- ->setEventTime(time())
- ->setEventSourceUrl($this->request->url())
- ->setUserData($this->userData);
-
- return $this->setEvent($event);
+ return $this->events;
}
- public function execute(): PromiseInterface
+ public function clearEvents(): self
+ {
+ return $this->setEvents([]);
+ }
+
+ public function sendEvents(): PromiseInterface
{
$eventRequest = (new EventRequestAsync(config('conversions-api.pixel_id')))
- ->setEvents([$this->event]);
+ ->setEvents($this->events);
if ($testCode = config('conversions-api.test_code')) {
$eventRequest->setTestEventCode($testCode);
@@ -92,9 +76,4 @@ public function execute(): PromiseInterface
return $eventRequest->execute();
}
-
- public function executePageViewEvent(): PromiseInterface
- {
- return $this->setEventByName('PageView')->execute();
- }
}
diff --git a/src/ConversionsApiServiceProvider.php b/src/ConversionsApiServiceProvider.php
index 2aaa46f..b908b87 100644
--- a/src/ConversionsApiServiceProvider.php
+++ b/src/ConversionsApiServiceProvider.php
@@ -2,7 +2,15 @@
namespace Esign\ConversionsApi;
-use Illuminate\Support\Facades\Blade;
+use Esign\ConversionsApi\Facades\ConversionsApi;
+use Esign\ConversionsApi\View\Components\DataLayerPageView;
+use Esign\ConversionsApi\View\Components\DataLayerUserDataVariable;
+use Esign\ConversionsApi\View\Components\DataLayerVariable;
+use Esign\ConversionsApi\View\Components\FacebookPixelPageView;
+use Esign\ConversionsApi\View\Components\FacebookPixelScript;
+use Esign\ConversionsApi\View\Components\FacebookPixelTrackingEvent;
+use Esign\ConversionsApi\View\Components\GoogleTagManagerBody;
+use Esign\ConversionsApi\View\Components\GoogleTagManagerHead;
use Illuminate\Support\ServiceProvider;
class ConversionsApiServiceProvider extends ServiceProvider
@@ -10,7 +18,16 @@ class ConversionsApiServiceProvider extends ServiceProvider
public function boot()
{
$this->loadViewsFrom($this->viewPath(), 'conversions-api');
- $this->registerBladeDirectives();
+ $this->loadViewComponentsAs('conversions-api', [
+ 'data-layer-page-view' => DataLayerPageView::class,
+ 'data-layer-variable' => DataLayerVariable::class,
+ 'data-layer-user-variable' => DataLayerUserDataVariable::class,
+ 'facebook-pixel-script' => FacebookPixelScript::class,
+ 'facebook-pixel-page-view' => FacebookPixelPageView::class,
+ 'facebook-pixel-tracking-event' => FacebookPixelTrackingEvent::class,
+ 'google-tag-manager-body' => GoogleTagManagerBody::class,
+ 'google-tag-manager-head' => GoogleTagManagerHead::class,
+ ]);
if ($this->app->runningInConsole()) {
$this->publishes([$this->configPath() => config_path('conversions-api.php')], 'config');
@@ -23,23 +40,6 @@ public function register()
$this->app->singleton(ConversionsApi::class);
}
- protected function registerBladeDirectives(): void
- {
- Blade::directive('conversionsApiDataLayer', function (?string $dataLayerVariableName = null) {
- if (! $dataLayerVariableName) {
- return "";
- }
-
- return " $dataLayerVariableName]); ?>";
- });
- Blade::directive('conversionsApiFacebookPixelScript', function () {
- return "";
- });
- Blade::directive('conversionsApiPageView', function () {
- return "executePageViewEvent(); ?>";
- });
- }
-
protected function configPath(): string
{
return __DIR__ . '/../config/conversions-api.php';
diff --git a/src/Facades/ConversionsApi.php b/src/Facades/ConversionsApi.php
index 2ee9a56..9e88e2a 100644
--- a/src/Facades/ConversionsApi.php
+++ b/src/Facades/ConversionsApi.php
@@ -5,6 +5,15 @@
use Illuminate\Support\Facades\Facade;
/**
+ * @method static self setUserData(\FacebookAds\Object\ServerSide\UserData $userData)
+ * @method static \FacebookAds\Object\ServerSide\UserData getUserData()
+ * @method static self addEvent(\FacebookAds\Object\ServerSide\Event $event)
+ * @method static self addEvents(iterable $events)
+ * @method static self setEvents(iterable $events)
+ * @method static \Illuminate\Support\Collection getEvents()
+ * @method static self clearEvents()
+ * @method static \GuzzleHttp\Promise\PromiseInterface sendEvents()
+ *
* @see \Esign\ConversionsApi\ConversionsApi
*/
class ConversionsApi extends Facade
diff --git a/src/Objects/DefaultUserData.php b/src/Objects/DefaultUserData.php
new file mode 100644
index 0000000..0b2fc71
--- /dev/null
+++ b/src/Objects/DefaultUserData.php
@@ -0,0 +1,17 @@
+setFbp(request()->cookie('_fbp'))
+ ->setFbc(request()->cookie('_fbc'))
+ ->setClientIpAddress(request()->ip())
+ ->setClientUserAgent(request()->userAgent());
+ }
+}
diff --git a/src/Objects/PageViewEvent.php b/src/Objects/PageViewEvent.php
new file mode 100644
index 0000000..029b7ab
--- /dev/null
+++ b/src/Objects/PageViewEvent.php
@@ -0,0 +1,52 @@
+setActionSource(ActionSource::WEBSITE)
+ ->setEventName('PageView')
+ ->setEventId((string) Str::uuid())
+ ->setEventTime(time())
+ ->setEventSourceUrl(request()->fullUrl())
+ ->setUserData(ConversionsApi::getUserData());
+ }
+
+ public function getFacebookPixelEventType(): string
+ {
+ return 'track';
+ }
+
+ public function getFacebookPixelEventName(): string
+ {
+ return $this->getEventName();
+ }
+
+ public function getFacebookPixelCustomData(): array
+ {
+ return [];
+ }
+
+ public function getFacebookPixelEventData(): array
+ {
+ return ['eventID' => $this->getEventId()];
+ }
+
+ public function getDataLayerArguments(): array
+ {
+ return [
+ 'event' => 'conversionsApiPageView',
+ 'conversionsApiPageViewEventId' => $this->getEventId()
+ ];
+ }
+}
diff --git a/src/View/Components/DataLayerPageView.php b/src/View/Components/DataLayerPageView.php
new file mode 100644
index 0000000..f95d22d
--- /dev/null
+++ b/src/View/Components/DataLayerPageView.php
@@ -0,0 +1,17 @@
+addEvent($pageViewEvent)->sendEvents();
+
+ parent::__construct($pageViewEvent);
+ }
+}
diff --git a/src/View/Components/DataLayerUserDataVariable.php b/src/View/Components/DataLayerUserDataVariable.php
new file mode 100644
index 0000000..384a1cc
--- /dev/null
+++ b/src/View/Components/DataLayerUserDataVariable.php
@@ -0,0 +1,33 @@
+ $userData->getEmail(),
+ 'conversionsApiUserFirstName' => $userData->getFirstName(),
+ 'conversionsApiUserLastName' => $userData->getLastName(),
+ 'conversionsApiUserPhone' => $userData->getPhone(),
+ 'conversionsApiUserExternalId' => $userData->getExternalId(),
+ 'conversionsApiUserGender' => $userData->getGender(),
+ 'conversionsApiUserDateOfBirth' => $userData->getDateOfBirth(),
+ 'conversionsApiUserCity' => $userData->getCity(),
+ 'conversionsApiUserState' => $userData->getState(),
+ 'conversionsApiUserZipCode' => $userData->getZipCode(),
+ 'conversionsApiUserCountry' => $userData->getCountryCode(),
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/src/View/Components/DataLayerVariable.php b/src/View/Components/DataLayerVariable.php
new file mode 100644
index 0000000..76d5922
--- /dev/null
+++ b/src/View/Components/DataLayerVariable.php
@@ -0,0 +1,21 @@
+ $this->event->getDataLayerArguments(),
+ ]);
+ }
+}
diff --git a/src/View/Components/FacebookPixelPageView.php b/src/View/Components/FacebookPixelPageView.php
new file mode 100644
index 0000000..4dbbccd
--- /dev/null
+++ b/src/View/Components/FacebookPixelPageView.php
@@ -0,0 +1,17 @@
+addEvent($pageViewEvent)->sendEvents();
+
+ parent::__construct($pageViewEvent);
+ }
+}
diff --git a/src/View/Components/FacebookPixelScript.php b/src/View/Components/FacebookPixelScript.php
new file mode 100644
index 0000000..6c87e36
--- /dev/null
+++ b/src/View/Components/FacebookPixelScript.php
@@ -0,0 +1,52 @@
+pixelId = $pixelId ?? config('conversions-api.pixel_id');
+ $this->advancedMatchingData = $advancedMatchingData ?? $this->getAdvancedMatchingDataFromConversionsApiUserData();
+ }
+
+ protected function getAdvancedMatchingDataFromConversionsApiUserData(): array
+ {
+ $userData = ConversionsApi::getUserData();
+
+ return array_filter([
+ 'em' => $userData->getEmail(),
+ 'fn' => $userData->getFirstName(),
+ 'ln' => $userData->getLastName(),
+ 'ph' => $userData->getPhone(),
+ 'external_id' => $userData->getExternalId(),
+ 'ge' => $userData->getGender(),
+ 'db' => $userData->getDateOfBirth(),
+ 'ct' => $userData->getCity(),
+ 'st' => $userData->getState(),
+ 'zp' => $userData->getZipCode(),
+ 'country' => $userData->getCountryCode(),
+ ]);
+ }
+
+ public function render()
+ {
+ return view('conversions-api::components.facebook-pixel-script', [
+ 'pixelId' => $this->pixelId,
+ 'advancedMatchingData' => $this->advancedMatchingData,
+ ]);
+ }
+}
diff --git a/src/View/Components/FacebookPixelTrackingEvent.php b/src/View/Components/FacebookPixelTrackingEvent.php
new file mode 100644
index 0000000..a3ca96c
--- /dev/null
+++ b/src/View/Components/FacebookPixelTrackingEvent.php
@@ -0,0 +1,24 @@
+ $this->event->getFacebookPixelEventType(),
+ 'eventName' => $this->event->getFacebookPixelEventName(),
+ 'customData' => $this->event->getFacebookPixelCustomData(),
+ 'eventData' => $this->event->getFacebookPixelEventData(),
+ ]);
+ }
+}
diff --git a/src/View/Components/GoogleTagManagerBody.php b/src/View/Components/GoogleTagManagerBody.php
new file mode 100644
index 0000000..2009a9b
--- /dev/null
+++ b/src/View/Components/GoogleTagManagerBody.php
@@ -0,0 +1,22 @@
+gtmId = $gtmId ?? config('conversions-api.gtm_id');
+ }
+
+ public function render()
+ {
+ return view('conversions-api::components.google-tag-manager-body', [
+ 'gtmId' => $this->gtmId,
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/src/View/Components/GoogleTagManagerHead.php b/src/View/Components/GoogleTagManagerHead.php
new file mode 100644
index 0000000..c820e47
--- /dev/null
+++ b/src/View/Components/GoogleTagManagerHead.php
@@ -0,0 +1,22 @@
+gtmId = $gtmId ?? config('conversions-api.gtm_id');
+ }
+
+ public function render()
+ {
+ return view('conversions-api::components.google-tag-manager-head', [
+ 'gtmId' => $this->gtmId,
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/tests/ConversionsApiTest.php b/tests/ConversionsApiTest.php
deleted file mode 100644
index 968d9c6..0000000
--- a/tests/ConversionsApiTest.php
+++ /dev/null
@@ -1,75 +0,0 @@
-assertTrue(Str::isUuid(ConversionsApi::getEventId()));
- }
-
- /** @test */
- public function it_can_set_an_event_id()
- {
- ConversionsApi::setEventId('abc');
-
- $this->assertEquals('abc', ConversionsApi::getEventId());
- }
-
- /** @test */
- public function it_can_set_user_data_by_default()
- {
- request()->headers->set('USER_AGENT', 'Esign Agent');
- request()->server->set('REMOTE_ADDR', '0.0.0.0');
-
- $this->assertEquals('0.0.0.0', ConversionsApi::getUserData()->getClientIpAddress());
- $this->assertEquals('Esign Agent', ConversionsApi::getUserData()->getClientUserAgent());
- }
-
- /** @test */
- public function it_can_set_user_data()
- {
- ConversionsApi::setUserData(
- (new UserData())->setFirstName('John')->setLastName('Doe')
- );
-
- $this->assertEquals('John', ConversionsApi::getUserData()->getFirstName());
- $this->assertEquals('Doe', ConversionsApi::getUserData()->getLastName());
- }
-
- /** @test */
- public function it_wont_have_an_event_by_default()
- {
- $this->assertNull(ConversionsApi::getEvent());
- }
-
- /** @test */
- public function it_can_set_an_event()
- {
- ConversionsApi::setEvent(
- (new Event())->setEventName('PageView')->setEventId('abc')
- );
-
- $this->assertEquals('PageView', ConversionsApi::getEvent()->getEventName());
- $this->assertEquals('abc', ConversionsApi::getEvent()->getEventId());
- }
-
- /** @test */
- public function it_can_set_an_event_by_name()
- {
- request()->headers->set('HOST', 'www.esign.eu');
- request()->server->set('HTTPS', true);
- ConversionsApi::setEventByName('Contact');
-
- $this->assertEquals('Contact', ConversionsApi::getEvent()->getEventName());
- $this->assertEquals('website', ConversionsApi::getEvent()->getActionSource());
- $this->assertEquals('https://www.esign.eu', ConversionsApi::getEvent()->getEventSourceUrl());
- }
-}
diff --git a/tests/ConversionsApiViewTest.php b/tests/ConversionsApiViewTest.php
deleted file mode 100644
index 822db87..0000000
--- a/tests/ConversionsApiViewTest.php
+++ /dev/null
@@ -1,89 +0,0 @@
-assertSame(
- 'executePageViewEvent(); ?>',
- Blade::compileString('@conversionsApiPageView'),
- );
- }
-
- /** @test */
- public function it_can_render_the_data_layer_view()
- {
- $this->assertStringContainsString(
- ConversionsApi::getEventId(),
- view('conversions-api::data-layer')->render()
- );
- }
-
- /** @test */
- public function it_can_contain_a_default_data_layer_variable_name()
- {
- $this->assertStringContainsString(
- 'conversionsApiEventId',
- view('conversions-api::data-layer')->render()
- );
- }
-
- /** @test */
- public function it_can_use_a_custom_data_layer_variable_name()
- {
- $this->assertStringContainsString(
- 'customDataLayerVariableName',
- view('conversions-api::data-layer', ['dataLayerVariableName' => 'customDataLayerVariableName'])->render()
- );
- }
-
- /** @test */
- public function it_can_render_the_facebook_pixel_script_view()
- {
- Config::set('conversions-api.pixel_id', 'your-pixel-id');
-
- $this->assertStringContainsString(
- ConversionsApi::getEventId(),
- view('conversions-api::facebook-pixel-script')->render()
- );
-
- $this->assertStringContainsString(
- 'your-pixel-id',
- view('conversions-api::facebook-pixel-script')->render()
- );
- }
-
- /** @test */
- public function it_can_render_the_facebook_pixel_script_directive()
- {
- $this->assertStringContainsString(
- "",
- Blade::compileString('@conversionsApiFacebookPixelScript'),
- );
- }
-
- /** @test */
- public function it_can_render_the_data_layer_directive()
- {
- $this->assertStringContainsString(
- " 'customDataLayerVariableName']); ?>",
- Blade::compileString("@conversionsApiDataLayer('customDataLayerVariableName')"),
- );
- }
-
- /** @test */
- public function it_can_render_the_data_layer_directive_when_no_arguments_are_provided()
- {
- $this->assertStringContainsString(
- "",
- Blade::compileString("@conversionsApiDataLayer"),
- );
- }
-}
diff --git a/tests/Feature/ConversionsApiTest.php b/tests/Feature/ConversionsApiTest.php
new file mode 100644
index 0000000..b47db12
--- /dev/null
+++ b/tests/Feature/ConversionsApiTest.php
@@ -0,0 +1,100 @@
+headers->set('USER_AGENT', 'Esign Agent');
+ request()->server->set('REMOTE_ADDR', '0.0.0.0');
+
+ $this->assertEquals('0.0.0.0', ConversionsApi::getUserData()->getClientIpAddress());
+ $this->assertEquals('Esign Agent', ConversionsApi::getUserData()->getClientUserAgent());
+ }
+
+ /** @test */
+ public function it_can_set_user_data()
+ {
+ ConversionsApi::setUserData(
+ (new UserData())->setFirstName('John')->setLastName('Doe')
+ );
+
+ $this->assertEquals('John', ConversionsApi::getUserData()->getFirstName());
+ $this->assertEquals('Doe', ConversionsApi::getUserData()->getLastName());
+ }
+
+ /** @test */
+ public function it_can_add_an_event()
+ {
+ ConversionsApi::addEvent(
+ (new Event())->setEventName('PageView')->setEventId('abc')
+ );
+
+ $this->assertCount(1, ConversionsApi::getEvents());
+ $this->assertEquals('PageView', ConversionsApi::getEvents()->first()->getEventName());
+ $this->assertEquals('abc', ConversionsApi::getEvents()->first()->getEventId());
+ }
+
+ /** @test */
+ public function it_can_add_multiple_events()
+ {
+ ConversionsApi::addEvent(
+ (new Event())->setEventName('PageView')->setEventId('abc')
+ );
+ ConversionsApi::addEvents([
+ (new Event())->setEventName('PageView')->setEventId('xyz'),
+ ]);
+
+ $this->assertCount(2, ConversionsApi::getEvents());
+ $this->assertEquals('PageView', ConversionsApi::getEvents()->first()->getEventName());
+ $this->assertEquals('abc', ConversionsApi::getEvents()->first()->getEventId());
+ }
+
+ /** @test */
+ public function it_can_set_an_array_of_events()
+ {
+ ConversionsApi::addEvent(
+ (new Event())->setEventName('PageView')->setEventId('abc')
+ );
+ ConversionsApi::setEvents([
+ (new Event())->setEventName('PageView')->setEventId('xyz'),
+ ]);
+
+ $this->assertCount(1, ConversionsApi::getEvents());
+ $this->assertEquals('PageView', ConversionsApi::getEvents()->first()->getEventName());
+ $this->assertEquals('xyz', ConversionsApi::getEvents()->first()->getEventId());
+ }
+
+ /** @test */
+ public function it_can_clear_events()
+ {
+ ConversionsApi::setEvents([
+ (new Event())->setEventName('PageView')->setEventId('abc'),
+ ]);
+
+ ConversionsApi::clearEvents();
+
+ $this->assertCount(0, ConversionsApi::getEvents());
+ }
+
+ /** @test */
+ public function it_can_get_events()
+ {
+ ConversionsApi::setEvents([
+ (new Event())->setEventName('PageView')->setEventId('abc'),
+ ]);
+
+ $events = ConversionsApi::getEvents();
+
+ $this->assertInstanceOf(EventCollection::class, $events);
+ $this->assertCount(1, $events);
+ }
+}
diff --git a/tests/Feature/View/Components/DataLayerPageViewTest.php b/tests/Feature/View/Components/DataLayerPageViewTest.php
new file mode 100644
index 0000000..2cce774
--- /dev/null
+++ b/tests/Feature/View/Components/DataLayerPageViewTest.php
@@ -0,0 +1,40 @@
+ 'b13ddf8f-df2d-4554-9ae6-a1a73861b0ad');
+ $component = $this->component(DataLayerPageView::class);
+
+ $component->assertSee(
+ 'window.dataLayer.push({"event":"conversionsApiPageView","conversionsApiPageViewEventId":"b13ddf8f-df2d-4554-9ae6-a1a73861b0ad"});',
+ false
+ );
+ }
+
+ /** @test */
+ public function it_can_execute_a_page_view_event()
+ {
+ $this->mock(ConversionsApi::class, function (MockInterface $mock) {
+ $mock->shouldReceive('getUserData')->once();
+ $mock->shouldReceive('clearEvents')->once()->andReturnSelf();
+ $mock->shouldReceive('addEvent')->once()->andReturnSelf();
+ $mock->shouldReceive('sendEvents')->once();
+ });
+
+ $this->component(DataLayerPageView::class);
+ }
+}
diff --git a/tests/Feature/View/Components/DataLayerUserDataVariableTest.php b/tests/Feature/View/Components/DataLayerUserDataVariableTest.php
new file mode 100644
index 0000000..1684bc8
--- /dev/null
+++ b/tests/Feature/View/Components/DataLayerUserDataVariableTest.php
@@ -0,0 +1,30 @@
+setEmail('test@test.com')
+ );
+ $component = $this->component(DataLayerUserDataVariable::class);
+
+ $component->assertSee(
+ 'window.dataLayer.push({"conversionsApiUserEmail":"test@test.com"});',
+ false
+ );
+ }
+}
diff --git a/tests/Feature/View/Components/DataLayerVariableTest.php b/tests/Feature/View/Components/DataLayerVariableTest.php
new file mode 100644
index 0000000..e0e6f88
--- /dev/null
+++ b/tests/Feature/View/Components/DataLayerVariableTest.php
@@ -0,0 +1,39 @@
+setEventId('9a97e3f0-3dbb-4d74-bf05-a42f330f843d');
+ $component = $this->component(DataLayerVariable::class, [
+ 'event' => $event,
+ ]);
+
+ $component->assertSee(
+ 'window.dataLayer.push({"event":"Contact","conversionsApiContactEventId":"9a97e3f0-3dbb-4d74-bf05-a42f330f843d"});',
+ false
+ );
+ }
+
+ /** @test */
+ public function it_can_render_anonymously()
+ {
+ $view = $this->blade('
+
+ ');
+
+ $view->assertSee('window.dataLayer.push({"event":"contact"});', false);
+ }
+}
diff --git a/tests/Feature/View/Components/FacebookPixelPageViewTest.php b/tests/Feature/View/Components/FacebookPixelPageViewTest.php
new file mode 100644
index 0000000..7560e34
--- /dev/null
+++ b/tests/Feature/View/Components/FacebookPixelPageViewTest.php
@@ -0,0 +1,40 @@
+ 'b13ddf8f-df2d-4554-9ae6-a1a73861b0ad');
+ $component = $this->component(FacebookPixelPageView::class);
+
+ $component->assertSee(
+ "fbq('track', 'PageView', {}, {\"eventID\":\"b13ddf8f-df2d-4554-9ae6-a1a73861b0ad\"}",
+ false
+ );
+ }
+
+ /** @test */
+ public function it_can_execute_a_page_view_event()
+ {
+ $this->mock(ConversionsApi::class, function (MockInterface $mock) {
+ $mock->shouldReceive('getUserData')->once();
+ $mock->shouldReceive('clearEvents')->once()->andReturnSelf();
+ $mock->shouldReceive('addEvent')->once()->andReturnSelf();
+ $mock->shouldReceive('sendEvents')->once();
+ });
+
+ $this->component(FacebookPixelPageView::class);
+ }
+}
diff --git a/tests/Feature/View/Components/FacebookPixelScriptTest.php b/tests/Feature/View/Components/FacebookPixelScriptTest.php
new file mode 100644
index 0000000..64bd643
--- /dev/null
+++ b/tests/Feature/View/Components/FacebookPixelScriptTest.php
@@ -0,0 +1,45 @@
+setEmail('test@test.com'));
+ $component = $this->component(FacebookPixelScript::class);
+
+ $component->assertSee("fbq('init', '414800860114807', {\"em\":\"test@test.com\"});", false);
+ }
+
+ /** @test */
+ public function it_can_render_an_empty_object_for_advanced_matching_data()
+ {
+ Config::set('conversions-api.pixel_id', '414800860114807');
+ $component = $this->component(FacebookPixelScript::class);
+
+ $component->assertSee("fbq('init', '414800860114807', {});", false);
+ }
+
+ /** @test */
+ public function it_can_render_the_view_passing_custom_data()
+ {
+ $component = $this->component(FacebookPixelScript::class, [
+ 'pixelId' => '744689831385767',
+ 'advancedMatchingData' => ['em' => 'test@test.com'],
+ ]);
+
+ $component->assertSee("fbq('init', '744689831385767', {\"em\":\"test@test.com\"});", false);
+ }
+}
diff --git a/tests/Feature/View/Components/FacebookPixelTrackingEventTest.php b/tests/Feature/View/Components/FacebookPixelTrackingEventTest.php
new file mode 100644
index 0000000..2d7e5f2
--- /dev/null
+++ b/tests/Feature/View/Components/FacebookPixelTrackingEventTest.php
@@ -0,0 +1,72 @@
+component(FacebookPixelTrackingEvent::class, [
+ 'event' => $event,
+ ]);
+
+ $component->assertSee('track');
+ $component->assertSee('Purchase');
+ }
+
+ /** @test */
+ public function it_can_encode_custom_data_and_event_data_as_objects_when_they_are_empty_arrays()
+ {
+ $event = (new PurchaseEvent());
+ $component = $this->component(FacebookPixelTrackingEvent::class, [
+ 'event' => $event,
+ ]);
+
+ $component->assertSee(
+ "fbq('track', 'Purchase', {}, {});",
+ false
+ );
+ }
+
+ /** @test */
+ public function it_can_json_encode_custom_data_and_event_data()
+ {
+ $contents = (new Content())->setProductId('10')->setQuantity(2);
+ $customData = (new CustomData())->setValue(120)->setCurrency('GBP')->setContents([$contents]);
+ $event = (new PurchaseEvent())->setCustomData($customData)->setEventId('123');
+ $component = $this->component(FacebookPixelTrackingEvent::class, [
+ 'event' => $event,
+ ]);
+
+ $component->assertSee(
+ "fbq('track', 'Purchase', {\"value\":120,\"currency\":\"GBP\",\"contents\":[{\"id\":\"10\",\"quantity\":2}]}, {\"eventID\":\"123\"});",
+ false
+ );
+ }
+
+ /** @test */
+ public function it_can_render_anonymously()
+ {
+ $view = $this->blade('
+
+ ');
+
+ $view->assertSee("fbq('track', 'Purchase', {}, {});", false);
+ }
+}
diff --git a/tests/Feature/View/Components/GoogleTagManagerBodyTest.php b/tests/Feature/View/Components/GoogleTagManagerBodyTest.php
new file mode 100644
index 0000000..2140ca5
--- /dev/null
+++ b/tests/Feature/View/Components/GoogleTagManagerBodyTest.php
@@ -0,0 +1,33 @@
+component(GoogleTagManagerBody::class);
+
+ $component->assertSee('https://www.googletagmanager.com/ns.html?id=GTM-123456');
+ }
+
+ /** @test */
+ public function it_can_render_the_view_using_custom_data()
+ {
+ Config::set('conversions-api.gtm_id', null);
+ $component = $this->component(GoogleTagManagerBody::class, [
+ 'gtmId' => 'GTM-123456',
+ ]);
+
+ $component->assertSee('https://www.googletagmanager.com/ns.html?id=GTM-123456');
+ }
+}
\ No newline at end of file
diff --git a/tests/Feature/View/Components/GoogleTagManagerHeadTest.php b/tests/Feature/View/Components/GoogleTagManagerHeadTest.php
new file mode 100644
index 0000000..b1360a3
--- /dev/null
+++ b/tests/Feature/View/Components/GoogleTagManagerHeadTest.php
@@ -0,0 +1,33 @@
+component(GoogleTagManagerHead::class);
+
+ $component->assertSee("(window,document,'script','dataLayer','GTM-123456')", false);
+ }
+
+ /** @test */
+ public function it_can_render_the_view_using_custom_data()
+ {
+ Config::set('conversions-api.gtm_id', null);
+ $component = $this->component(GoogleTagManagerHead::class, [
+ 'gtmId' => 'GTM-123456',
+ ]);
+
+ $component->assertSee("(window,document,'script','dataLayer','GTM-123456')", false);
+ }
+}
\ No newline at end of file
diff --git a/tests/Support/Events/ContactEvent.php b/tests/Support/Events/ContactEvent.php
new file mode 100644
index 0000000..befc699
--- /dev/null
+++ b/tests/Support/Events/ContactEvent.php
@@ -0,0 +1,17 @@
+ 'Contact',
+ 'conversionsApiContactEventId' => $this->getEventId(),
+ ];
+ }
+}
diff --git a/tests/Support/Events/PurchaseEvent.php b/tests/Support/Events/PurchaseEvent.php
new file mode 100644
index 0000000..2826313
--- /dev/null
+++ b/tests/Support/Events/PurchaseEvent.php
@@ -0,0 +1,41 @@
+getCustomData();
+
+ return array_filter([
+ 'value' => $customData?->getValue(),
+ 'currency' => $customData?->getCurrency(),
+ 'contents' => array_map(function (Content $content) {
+ return [
+ 'id' => $content->getProductId(),
+ 'quantity' => $content->getQuantity(),
+ ];
+ }, $customData?->getContents() ?? []),
+ ]);
+ }
+
+ public function getFacebookPixelEventData(): array
+ {
+ return array_filter(['eventID' => $this->getEventId()]);
+ }
+}
diff --git a/tests/Unit/Collections/EventCollectionTest.php b/tests/Unit/Collections/EventCollectionTest.php
new file mode 100644
index 0000000..2b4e982
--- /dev/null
+++ b/tests/Unit/Collections/EventCollectionTest.php
@@ -0,0 +1,39 @@
+filterFacebookPixelEvents();
+
+ $this->assertTrue($facebookPixelEvents->contains($purchaseEvent));
+ $this->assertFalse($facebookPixelEvents->contains($contactEvent));
+ }
+
+ /** @test */
+ public function it_can_filter_data_layer_events()
+ {
+ $eventCollection = new EventCollection([
+ $purchaseEvent = new PurchaseEvent(),
+ $contactEvent = new ContactEvent(),
+ ]);
+
+ $dataLayerEvents = $eventCollection->filterDataLayerEvents();
+
+ $this->assertFalse($dataLayerEvents->contains($purchaseEvent));
+ $this->assertTrue($dataLayerEvents->contains($contactEvent));
+ }
+}
diff --git a/tests/Unit/Objects/PageViewEventTest.php b/tests/Unit/Objects/PageViewEventTest.php
new file mode 100644
index 0000000..eb35637
--- /dev/null
+++ b/tests/Unit/Objects/PageViewEventTest.php
@@ -0,0 +1,27 @@
+get('posts?title=abc');
+ Str::createUuidsUsing(fn () => 'b13ddf8f-df2d-4554-9ae6-a1a73861b0ad');
+
+ $event = PageViewEvent::create();
+
+ $this->assertEquals(ActionSource::WEBSITE, $event->getActionSource());
+ $this->assertEquals('PageView', $event->getEventName());
+ $this->assertEquals('b13ddf8f-df2d-4554-9ae6-a1a73861b0ad', $event->getEventId());
+ $this->assertEquals(ConversionsApi::getUserData(), $event->getUserData());
+ $this->assertEquals('http://localhost/posts?title=abc', $event->getEventSourceUrl());
+ }
+}
\ No newline at end of file