├── .gitignore
├── src
├── Contracts
│ ├── MapsToDataLayer.php
│ └── MapsToFacebookPixel.php
├── Objects
│ ├── DefaultUserData.php
│ └── PageViewEvent.php
├── View
│ └── Components
│ │ ├── DataLayerPageView.php
│ │ ├── FacebookPixelPageView.php
│ │ ├── DataLayerVariable.php
│ │ ├── GoogleTagManagerBody.php
│ │ ├── GoogleTagManagerHead.php
│ │ ├── FacebookPixelTrackingEvent.php
│ │ ├── DataLayerUserDataVariable.php
│ │ └── FacebookPixelScript.php
├── Collections
│ └── EventCollection.php
├── Facades
│ └── ConversionsApi.php
├── ConversionsApi.php
└── ConversionsApiServiceProvider.php
├── resources
└── views
│ └── components
│ ├── google-tag-manager-body.blade.php
│ ├── data-layer-variable.blade.php
│ ├── facebook-pixel-tracking-event.blade.php
│ ├── google-tag-manager-head.blade.php
│ └── facebook-pixel-script.blade.php
├── tests
├── TestCase.php
├── Support
│ └── Events
│ │ ├── ContactEvent.php
│ │ └── PurchaseEvent.php
├── Unit
│ ├── Objects
│ │ └── PageViewEventTest.php
│ └── Collections
│ │ └── EventCollectionTest.php
└── Feature
│ ├── View
│ └── Components
│ │ ├── DataLayerUserDataVariableTest.php
│ │ ├── GoogleTagManagerBodyTest.php
│ │ ├── GoogleTagManagerHeadTest.php
│ │ ├── FacebookPixelPageViewTest.php
│ │ ├── DataLayerPageViewTest.php
│ │ ├── DataLayerVariableTest.php
│ │ ├── FacebookPixelScriptTest.php
│ │ └── FacebookPixelTrackingEventTest.php
│ └── ConversionsApiTest.php
├── phpunit.xml
├── config
└── conversions-api.php
├── .github
├── pull_request_template.md
└── workflows
│ └── main.yml
├── LICENSE.md
├── .php-cs-fixer.php
├── composer.json
├── UPGRADING.md
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor
2 | composer.lock
3 | .phpunit.result.cache
4 | .php-cs-fixer.cache
5 | .phpunit.cache
--------------------------------------------------------------------------------
/src/Contracts/MapsToDataLayer.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/resources/views/components/data-layer-variable.blade.php:
--------------------------------------------------------------------------------
1 | @props(['arguments'])
2 |
3 |
--------------------------------------------------------------------------------
/resources/views/components/facebook-pixel-tracking-event.blade.php:
--------------------------------------------------------------------------------
1 | @props([
2 | 'eventType',
3 | 'eventName',
4 | 'customData',
5 | 'eventData',
6 | ])
7 |
8 |
--------------------------------------------------------------------------------
/tests/TestCase.php:
--------------------------------------------------------------------------------
1 | (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
2 | new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
3 | j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
4 | 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
5 | })(window,document,'script','dataLayer','{{ $gtmId }}');
--------------------------------------------------------------------------------
/tests/Support/Events/ContactEvent.php:
--------------------------------------------------------------------------------
1 | 'Contact',
14 | 'conversionsApiContactEventId' => $this->getEventId(),
15 | ];
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Objects/DefaultUserData.php:
--------------------------------------------------------------------------------
1 | setFbp(request()->cookie('_fbp'))
13 | ->setFbc(request()->cookie('_fbc'))
14 | ->setClientIpAddress(request()->ip())
15 | ->setClientUserAgent(request()->userAgent());
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/View/Components/DataLayerPageView.php:
--------------------------------------------------------------------------------
1 | addEvent($pageViewEvent)->sendEvents();
14 |
15 | parent::__construct($pageViewEvent);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/View/Components/FacebookPixelPageView.php:
--------------------------------------------------------------------------------
1 | addEvent($pageViewEvent)->sendEvents();
14 |
15 | parent::__construct($pageViewEvent);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/View/Components/DataLayerVariable.php:
--------------------------------------------------------------------------------
1 | $this->event->getDataLayerArguments(),
19 | ]);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/View/Components/GoogleTagManagerBody.php:
--------------------------------------------------------------------------------
1 | gtmId = $gtmId ?? config('conversions-api.gtm_id');
14 | }
15 |
16 | public function render()
17 | {
18 | return view('conversions-api::components.google-tag-manager-body', [
19 | 'gtmId' => $this->gtmId,
20 | ]);
21 | }
22 | }
--------------------------------------------------------------------------------
/src/View/Components/GoogleTagManagerHead.php:
--------------------------------------------------------------------------------
1 | gtmId = $gtmId ?? config('conversions-api.gtm_id');
14 | }
15 |
16 | public function render()
17 | {
18 | return view('conversions-api::components.google-tag-manager-head', [
19 | 'gtmId' => $this->gtmId,
20 | ]);
21 | }
22 | }
--------------------------------------------------------------------------------
/src/Collections/EventCollection.php:
--------------------------------------------------------------------------------
1 | filter(fn ($event) => $event instanceof MapsToFacebookPixel);
14 | }
15 |
16 | public function filterDataLayerEvents(): static
17 | {
18 | return $this->filter(fn ($event) => $event instanceof MapsToDataLayer);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/resources/views/components/facebook-pixel-script.blade.php:
--------------------------------------------------------------------------------
1 | @props([
2 | 'pixelId',
3 | 'advancedMatchingData',
4 | ])
5 |
6 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 | tests
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | src/
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/View/Components/FacebookPixelTrackingEvent.php:
--------------------------------------------------------------------------------
1 | $this->event->getFacebookPixelEventType(),
19 | 'eventName' => $this->event->getFacebookPixelEventName(),
20 | 'customData' => $this->event->getFacebookPixelCustomData(),
21 | 'eventData' => $this->event->getFacebookPixelEventData(),
22 | ]);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/config/conversions-api.php:
--------------------------------------------------------------------------------
1 | env('CONVERSIONS_API_ACCESS_TOKEN'),
8 |
9 | /**
10 | * The pixel ID used by the Conversions API.
11 | */
12 | 'pixel_id' => env('CONVERSIONS_API_PIXEL_ID'),
13 |
14 | /**
15 | * The Google Tag Manager container ID used in case you're deduplicating
16 | * events through Google Tag Manager instead of Facebook Pixel directly.
17 | * Should look something like "GTM-XXXXXX".
18 | */
19 | 'gtm_id' => env('GOOGLE_TAG_MANAGER_ID'),
20 |
21 | /**
22 | * The Conversions API comes with a nice way to test your events.
23 | * You may use this config variable to set your test code.
24 | */
25 | 'test_code' => env('CONVERSIONS_API_TEST_CODE'),
26 | ];
27 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 |
2 | ## Description
3 |
4 |
5 | ## How has this been tested?
6 | - [ ] Unit tests
7 | - [ ] Integration tests
8 | - [ ] Manual testing
9 |
10 | ## Type of change
11 | - [ ] Bug fix
12 | - [ ] New feature
13 | - [ ] Breaking change
14 | - [ ] Documentation update
15 | - [ ] Other (please describe):
16 |
17 | ## Checklist
18 |
19 |
20 | - [ ] I have performed a self-review of my own code.
21 | - [ ] I have commented my code, particularly in hard-to-understand areas.
22 | - [ ] I have made corresponding changes to the documentation if necessary.
23 | - [ ] I have added tests that prove my fix is effective or that my feature works.
24 |
--------------------------------------------------------------------------------
/src/Facades/ConversionsApi.php:
--------------------------------------------------------------------------------
1 |
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/tests/Unit/Objects/PageViewEventTest.php:
--------------------------------------------------------------------------------
1 | get('posts?title=abc');
18 | Str::createUuidsUsing(fn () => 'b13ddf8f-df2d-4554-9ae6-a1a73861b0ad');
19 |
20 | $event = PageViewEvent::create();
21 |
22 | $this->assertEquals(ActionSource::WEBSITE, $event->getActionSource());
23 | $this->assertEquals('PageView', $event->getEventName());
24 | $this->assertEquals('b13ddf8f-df2d-4554-9ae6-a1a73861b0ad', $event->getEventId());
25 | $this->assertEquals(ConversionsApi::getUserData(), $event->getUserData());
26 | $this->assertEquals('http://localhost/posts?title=abc', $event->getEventSourceUrl());
27 | }
28 | }
--------------------------------------------------------------------------------
/tests/Feature/View/Components/DataLayerUserDataVariableTest.php:
--------------------------------------------------------------------------------
1 | setEmail('test@test.com')
23 | );
24 | $component = $this->component(DataLayerUserDataVariable::class);
25 |
26 | $component->assertSee(
27 | 'window.dataLayer.push({"conversionsApiUserEmail":"test@test.com"});',
28 | false
29 | );
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/tests/Feature/View/Components/GoogleTagManagerBodyTest.php:
--------------------------------------------------------------------------------
1 | component(GoogleTagManagerBody::class);
20 |
21 | $component->assertSee('https://www.googletagmanager.com/ns.html?id=GTM-123456');
22 | }
23 |
24 | #[Test]
25 | public function it_can_render_the_view_using_custom_data(): void
26 | {
27 | Config::set('conversions-api.gtm_id', null);
28 | $component = $this->component(GoogleTagManagerBody::class, [
29 | 'gtmId' => 'GTM-123456',
30 | ]);
31 |
32 | $component->assertSee('https://www.googletagmanager.com/ns.html?id=GTM-123456');
33 | }
34 | }
--------------------------------------------------------------------------------
/tests/Feature/View/Components/GoogleTagManagerHeadTest.php:
--------------------------------------------------------------------------------
1 | component(GoogleTagManagerHead::class);
20 |
21 | $component->assertSee("(window,document,'script','dataLayer','GTM-123456')", false);
22 | }
23 |
24 | #[Test]
25 | public function it_can_render_the_view_using_custom_data(): void
26 | {
27 | Config::set('conversions-api.gtm_id', null);
28 | $component = $this->component(GoogleTagManagerHead::class, [
29 | 'gtmId' => 'GTM-123456',
30 | ]);
31 |
32 | $component->assertSee("(window,document,'script','dataLayer','GTM-123456')", false);
33 | }
34 | }
--------------------------------------------------------------------------------
/tests/Support/Events/PurchaseEvent.php:
--------------------------------------------------------------------------------
1 | getCustomData();
24 |
25 | return array_filter([
26 | 'value' => $customData?->getValue(),
27 | 'currency' => $customData?->getCurrency(),
28 | 'contents' => array_map(function (Content $content) {
29 | return [
30 | 'id' => $content->getProductId(),
31 | 'quantity' => $content->getQuantity(),
32 | ];
33 | }, $customData?->getContents() ?? []),
34 | ]);
35 | }
36 |
37 | public function getFacebookPixelEventData(): array
38 | {
39 | return array_filter(['eventID' => $this->getEventId()]);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/View/Components/DataLayerUserDataVariable.php:
--------------------------------------------------------------------------------
1 | $userData->getEmail(),
21 | 'conversionsApiUserFirstName' => $userData->getFirstName(),
22 | 'conversionsApiUserLastName' => $userData->getLastName(),
23 | 'conversionsApiUserPhone' => $userData->getPhone(),
24 | 'conversionsApiUserExternalId' => $userData->getExternalId(),
25 | 'conversionsApiUserGender' => $userData->getGender(),
26 | 'conversionsApiUserDateOfBirth' => $userData->getDateOfBirth(),
27 | 'conversionsApiUserCity' => $userData->getCity(),
28 | 'conversionsApiUserState' => $userData->getState(),
29 | 'conversionsApiUserZipCode' => $userData->getZipCode(),
30 | 'conversionsApiUserCountry' => $userData->getCountryCode(),
31 | ]);
32 | }
33 | }
--------------------------------------------------------------------------------
/.php-cs-fixer.php:
--------------------------------------------------------------------------------
1 | in([
5 | __DIR__ . '/src',
6 | __DIR__ . '/config',
7 | __DIR__ . '/tests',
8 | ])
9 | ->name('*.php')
10 | ->notName('*.blade.php')
11 | ->ignoreDotFiles(true)
12 | ->ignoreVCS(true);
13 |
14 | return (new PhpCsFixer\Config())
15 | ->setRules([
16 | '@PSR12' => true,
17 | 'array_syntax' => ['syntax' => 'short'],
18 | 'ordered_imports' => ['sort_algorithm' => 'alpha'],
19 | 'no_unused_imports' => true,
20 | 'not_operator_with_successor_space' => true,
21 | 'trailing_comma_in_multiline' => [
22 | 'elements' => ['arrays'],
23 | ],
24 | 'unary_operator_spaces' => true,
25 | 'binary_operator_spaces' => true,
26 | 'blank_line_before_statement' => [
27 | 'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'],
28 | ],
29 | 'phpdoc_scalar' => true,
30 | 'phpdoc_single_line_var_spacing' => true,
31 | 'class_attributes_separation' => [
32 | 'elements' => [
33 | 'method' => 'one',
34 | ],
35 | ],
36 | 'method_argument_space' => [
37 | 'on_multiline' => 'ensure_fully_multiline',
38 | 'keep_multiple_spaces_after_comma' => true,
39 | ],
40 | 'single_trait_insert_per_statement' => true,
41 | ])
42 | ->setFinder($finder);
43 |
--------------------------------------------------------------------------------
/tests/Unit/Collections/EventCollectionTest.php:
--------------------------------------------------------------------------------
1 | filterFacebookPixelEvents();
22 |
23 | $this->assertTrue($facebookPixelEvents->contains($purchaseEvent));
24 | $this->assertFalse($facebookPixelEvents->contains($contactEvent));
25 | }
26 |
27 | #[Test]
28 | public function it_can_filter_data_layer_events(): void
29 | {
30 | $eventCollection = new EventCollection([
31 | $purchaseEvent = new PurchaseEvent(),
32 | $contactEvent = new ContactEvent(),
33 | ]);
34 |
35 | $dataLayerEvents = $eventCollection->filterDataLayerEvents();
36 |
37 | $this->assertFalse($dataLayerEvents->contains($purchaseEvent));
38 | $this->assertTrue($dataLayerEvents->contains($contactEvent));
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/tests/Feature/View/Components/FacebookPixelPageViewTest.php:
--------------------------------------------------------------------------------
1 | 'b13ddf8f-df2d-4554-9ae6-a1a73861b0ad');
21 | $component = $this->component(FacebookPixelPageView::class);
22 |
23 | $component->assertSee(
24 | "fbq('track', 'PageView', {}, {\"eventID\":\"b13ddf8f-df2d-4554-9ae6-a1a73861b0ad\"}",
25 | false
26 | );
27 | }
28 |
29 | #[Test]
30 | public function it_can_execute_a_page_view_event(): void
31 | {
32 | $this->mock(ConversionsApi::class, function (MockInterface $mock) {
33 | $mock->shouldReceive('getUserData')->once();
34 | $mock->shouldReceive('clearEvents')->once()->andReturnSelf();
35 | $mock->shouldReceive('addEvent')->once()->andReturnSelf();
36 | $mock->shouldReceive('sendEvents')->once();
37 | });
38 |
39 | $this->component(FacebookPixelPageView::class);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/tests/Feature/View/Components/DataLayerPageViewTest.php:
--------------------------------------------------------------------------------
1 | 'b13ddf8f-df2d-4554-9ae6-a1a73861b0ad');
21 | $component = $this->component(DataLayerPageView::class);
22 |
23 | $component->assertSee(
24 | 'window.dataLayer.push({"event":"conversionsApiPageView","conversionsApiPageViewEventId":"b13ddf8f-df2d-4554-9ae6-a1a73861b0ad"});',
25 | false
26 | );
27 | }
28 |
29 | #[Test]
30 | public function it_can_execute_a_page_view_event(): void
31 | {
32 | $this->mock(ConversionsApi::class, function (MockInterface $mock) {
33 | $mock->shouldReceive('getUserData')->once();
34 | $mock->shouldReceive('clearEvents')->once()->andReturnSelf();
35 | $mock->shouldReceive('addEvent')->once()->andReturnSelf();
36 | $mock->shouldReceive('sendEvents')->once();
37 | });
38 |
39 | $this->component(DataLayerPageView::class);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Objects/PageViewEvent.php:
--------------------------------------------------------------------------------
1 | setActionSource(ActionSource::WEBSITE)
18 | ->setEventName('PageView')
19 | ->setEventId((string) Str::uuid())
20 | ->setEventTime(time())
21 | ->setEventSourceUrl(request()->fullUrl())
22 | ->setUserData(ConversionsApi::getUserData());
23 | }
24 |
25 | public function getFacebookPixelEventType(): string
26 | {
27 | return 'track';
28 | }
29 |
30 | public function getFacebookPixelEventName(): string
31 | {
32 | return $this->getEventName();
33 | }
34 |
35 | public function getFacebookPixelCustomData(): array
36 | {
37 | return [];
38 | }
39 |
40 | public function getFacebookPixelEventData(): array
41 | {
42 | return ['eventID' => $this->getEventId()];
43 | }
44 |
45 | public function getDataLayerArguments(): array
46 | {
47 | return [
48 | 'event' => 'conversionsApiPageView',
49 | 'conversionsApiPageViewEventId' => $this->getEventId()
50 | ];
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/tests/Feature/View/Components/DataLayerVariableTest.php:
--------------------------------------------------------------------------------
1 | setEventId('9a97e3f0-3dbb-4d74-bf05-a42f330f843d');
19 | $component = $this->component(DataLayerVariable::class, [
20 | 'event' => $event,
21 | ]);
22 |
23 | $component->assertSee(
24 | 'window.dataLayer.push({"event":"Contact","conversionsApiContactEventId":"9a97e3f0-3dbb-4d74-bf05-a42f330f843d"});',
25 | false
26 | );
27 | }
28 |
29 | #[Test]
30 | public function it_can_render_anonymously(): void
31 | {
32 | $view = $this->blade('
33 |
36 | ');
37 |
38 | $view->assertSee('window.dataLayer.push({"event":"contact"});', false);
39 | }
40 |
41 | #[Test]
42 | public function it_can_pass_component_attributes(): void
43 | {
44 | $view = $this->blade('
45 |
49 | ');
50 |
51 | $view->assertSee('
172 | ```
173 |
174 | This package will attempt to provide as much advanced matching data as possible by using user data from the `ConversionsApi`.
175 | For example when an email address is set, it will automatically be provided to the init method:
176 | ```php
177 | ConversionsApi::setUserData(
178 | (new UserData())->setEmail('john@example.com')
179 | );
180 | ```
181 | ```js
182 | fbq('init', 'your-configured-pixel-id', {"em": "john@example.com"});
183 | ```
184 |
185 | Now that your Pixel is correctly initialized, it's time to send some events.
186 | 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).
187 | 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:
188 | ```php
189 | use Esign\ConversionsApi\Contracts\MapsToFacebookPixel;
190 | use Esign\ConversionsApi\Facades\ConversionsApi;
191 | use FacebookAds\Object\ServerSide\ActionSource;
192 | use FacebookAds\Object\ServerSide\Event;
193 |
194 | class PurchaseEvent extends Event implements MapsToFacebookPixel
195 | {
196 | public static function create(): static
197 | {
198 | return (new static())
199 | ->setActionSource(ActionSource::WEBSITE)
200 | ->setEventName('Purchase')
201 | ->setEventTime(time())
202 | ->setEventSourceUrl(request()->fullUrl())
203 | ->setEventId((string) Str::uuid())
204 | ->setUserData(ConversionsApi::getUserData());
205 | }
206 |
207 | public function getFacebookPixelEventType(): string
208 | {
209 | return 'track';
210 | }
211 |
212 | public function getFacebookPixelEventName(): string
213 | {
214 | return $this->getEventName();
215 | }
216 |
217 | public function getFacebookPixelCustomData(): array
218 | {
219 | $customData = $this->getCustomData();
220 |
221 | return array_filter([
222 | 'currency' => $customData?->getCurrency(),
223 | 'value' => $customData?->getValue(),
224 | ]);
225 | }
226 |
227 | public function getFacebookPixelEventData(): array
228 | {
229 | return array_filter(['eventID' => $this->getEventId()]);
230 | }
231 | }
232 | ```
233 |
234 | You may now pass any class that implements the `MapsToFacebookPixel` interface to the view component responsible for tracking Facebook Pixel events:
235 | ```php
236 | use FacebookAds\Object\ServerSide\CustomData;
237 | use Illuminate\Support\Str;
238 |
239 | $event = PurchaseEvent::create()->setCustomData(
240 | (new CustomData())->setCurrency('EUR')->setValue(10)
241 | );
242 | ```
243 |
244 | ```blade
245 |
246 | ```
247 |
248 | This will render the following script tag:
249 | ```html
250 |
253 | ```
254 |
255 | To retrieve a list of all events that implement the `MapsToFacebookPixel` interface you may call the `filterFacebookPixelEvents` method:
256 | ```blade
257 | @foreach(ConversionsApi::getEvents()->filterFacebookPixelEvents() as $event)
258 |
259 | @endforeach
260 | ```
261 |
262 | In case you want more control over what's being rendered, you may always use the anonymous component:
263 | ```blade
264 |
270 | ```
271 |
272 | ### Google Tag Manager
273 | Before attempting to deduplicate events through GTM make sure to configure your GTM container id and include the necessary scripts:
274 |
275 | ```env
276 | GOOGLE_TAG_MANAGER_ID=GTM-XXXXXX
277 | ```
278 |
279 | ```blade
280 |
281 |
282 |
283 | {{-- ... --}}
284 |
285 |
286 |
287 | {{-- ... --}}
288 |
289 |
290 | ```
291 |
292 | This package comes with a view component that will map all user data from the `ConversionsApi` to dataLayer variables:
293 | ```blade
294 |
295 | ```
296 | For example when an email address is set, it will be automatically mapped to a dataLayer variable.
297 | Check the [source](src/View/Components/DataLayerUserDataVariable.php) of the view component to see a list of all possible variables.
298 | ```php
299 | ConversionsApi::setUserData(
300 | (new UserData())->setEmail('john@example.com')
301 | );
302 | ```
303 | ```js
304 | window.dataLayer.push({"conversionsApiUserEmail": "john@example.com"});
305 | ```
306 |
307 | Now that your Pixel through GTM is correctly initialized, it's time to send some events.
308 | 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:
309 | ```php
310 | use Esign\ConversionsApi\Contracts\MapsToDataLayer;
311 | use Esign\ConversionsApi\Facades\ConversionsApi;
312 | use FacebookAds\Object\ServerSide\ActionSource;
313 | use FacebookAds\Object\ServerSide\Event;
314 |
315 | class PurchaseEvent extends Event implements MapsToDataLayer
316 | {
317 | public static function create(): static
318 | {
319 | return (new static())
320 | ->setActionSource(ActionSource::WEBSITE)
321 | ->setEventName('Purchase')
322 | ->setEventTime(time())
323 | ->setEventSourceUrl(request()->fullUrl())
324 | ->setEventId((string) Str::uuid())
325 | ->setUserData(ConversionsApi::getUserData());
326 | }
327 |
328 | public function getDataLayerArguments(): array
329 | {
330 | $customData = $this->getCustomData();
331 |
332 | return [
333 | 'event' => 'conversionsApiPurchase',
334 | 'conversionsApiPurchaseEventId' => $this->getEventId(),
335 | 'conversionsApiPurchaseCurrency' => $customData?->getCurrency(),
336 | 'conversionsApiPurchaseValue' => $customData?->getValue(),
337 | ];
338 | }
339 | }
340 | ```
341 |
342 | You may now pass any class that implements the `MapsToDataLayer` interface to the view component responsible for tracking Facebook Pixel events:
343 | ```php
344 | use FacebookAds\Object\ServerSide\CustomData;
345 | use Illuminate\Support\Str;
346 |
347 | $event = PurchaseEvent::create()->setCustomData(
348 | (new CustomData())->setCurrency('EUR')->setValue(10)
349 | );
350 | ```
351 |
352 | ```blade
353 |
354 | ```
355 |
356 | This will render the following script tag:
357 | ```html
358 |
366 | ```
367 |
368 | To retrieve a list of all events that implement the `MapsToDataLayer` interface you may call the `filterDataLayerEvents` method:
369 | ```blade
370 | @foreach(ConversionsApi::getEvents()->filterDataLayerEvents() as $event)
371 |
372 | @endforeach
373 | ```
374 |
375 | In case you want more control over what's being rendered, you may always use the anonymous component:
376 | ```blade
377 |
378 | ```
379 |
380 | ## PageView Events
381 | This package ships with some helpers to track `PageView` events out of the box.
382 | These helpers will automatically send both Conversions API & Facebook Pixel events and provide event deduplication.
383 | > **Note**
384 | > 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.
385 |
386 | In case you're using the Facebook Pixel directly:
387 | ```blade
388 |
389 | ```
390 | Or by using Google Tag Manager. The data-layer variable to deduplicate events is called `conversionsApiPageViewEventId`.
391 | ```blade
392 |
393 | ```
394 |
395 | ## Troubleshooting
396 | ### PageView events are not shown as deduplicated in the test events dashboard
397 | Event deduplication for PageView events should be fine out of the box, since the event name and event id parameters have been provided.
398 | However, when serving your application locally the ip address returned by Laravel's `request()->ip()` will be `127.0.0.1`.
399 | This is different from the ip address sent through Facebook Pixel, causing the Conversions API and Facebook Pixel events to not be deduplicated.
400 | This issue should solve itself once the application will be ran in production.
401 |
402 | ### Whitelisting Cookies in Laravel
403 | When using ``\App\Http\Middleware\EncryptCookies::class`` in ``\App\Http\Kernel::class``, ensure to whitelist _fbp and _fbc cookies to prevent null values in loading ``DefaultUserData::create()``. Update the $except property in the EncryptCookies middleware as shown below:
404 |
405 | ```php
406 |