├── .gitignore ├── .release-it.json ├── BingAds-Auth.md ├── BingAds-SDK.md ├── CHANGELOG.md ├── FacebookAds-Auth.md ├── FacebookAds-SDK.md ├── GoogleAds-Auth.md ├── GoogleAds-SDK.md ├── LICENSE ├── README.md ├── composer.json ├── config ├── bing-ads.php ├── facebook-ads.php └── google-ads.php ├── package-lock.json ├── package.json └── src ├── Console └── RefreshTokenCommand.php ├── Facades └── LaravelAds.php ├── LaravelAds.php ├── Providers └── LaravelAdsProvider.php └── Services ├── BingAds ├── Fetch.php ├── Operations │ ├── AdGroup.php │ ├── AdGroupOperations.php │ ├── Campaign.php │ ├── CampaignOperations.php │ ├── Customer.php │ ├── CustomerOperations.php │ ├── OfflineConversions.php │ └── Operation.php ├── ReportDownload.php ├── Reports.php └── Service.php ├── FacebookAds ├── Fetch.php ├── Reports.php └── Service.php └── GoogleAds ├── Fetch.php ├── Operations ├── AdGroup.php ├── AdGroupOperations.php ├── Campaign.php ├── CampaignOperations.php └── OfflineConversions.php ├── ReportDownload.php ├── Reports.php └── Service.php /.gitignore: -------------------------------------------------------------------------------- 1 | #------------------------- 2 | # Operating Specific Junk Files 3 | #------------------------- 4 | 5 | # OS X 6 | .DS_Store 7 | .AppleDouble 8 | .LSOverride 9 | 10 | # OS X Thumbnails 11 | ._* 12 | 13 | # Windows image file caches 14 | Thumbs.db 15 | ehthumbs.db 16 | Desktop.ini 17 | 18 | # Recycle Bin used on file shares 19 | $RECYCLE.BIN/ 20 | 21 | # Windows Installer files 22 | *.cab 23 | *.msi 24 | *.msm 25 | *.msp 26 | 27 | # Windows shortcuts 28 | *.lnk 29 | 30 | # Linux 31 | *~ 32 | 33 | # KDE directory preferences 34 | .directory 35 | 36 | # Linux trash folder which might appear on any partition or disk 37 | .Trash-* 38 | 39 | #------------------------- 40 | # Environment Files 41 | #------------------------- 42 | # These should never be under version control, 43 | # as it poses a security risk. 44 | .env 45 | .vagrant 46 | app/.env 47 | Vagrantfile 48 | 49 | #------------------------- 50 | # Composer 51 | #------------------------- 52 | /vendor 53 | composer.lock 54 | composer.phar 55 | 56 | #------------------------- 57 | # IDE / Development Files 58 | #------------------------- 59 | 60 | # Modules Testing 61 | _modules/* 62 | 63 | # phpenv local config 64 | .php-version 65 | 66 | # Jetbrains editors (PHPStorm, etc) 67 | .idea/ 68 | *.iml 69 | 70 | # Netbeans 71 | nbproject/ 72 | build/ 73 | nbbuild/ 74 | dist/ 75 | nbdist/ 76 | nbactions.xml 77 | nb-configuration.xml 78 | .nb-gradle/ 79 | 80 | # Sublime Text 81 | *.tmlanguage.cache 82 | *.tmPreferences.cache 83 | *.stTheme.cache 84 | *.sublime-workspace 85 | *.sublime-project 86 | .phpintel 87 | /api/ 88 | 89 | # Visual Studio Code 90 | .vscode/ 91 | node_modules 92 | -------------------------------------------------------------------------------- /.release-it.json: -------------------------------------------------------------------------------- 1 | { 2 | "github": { 3 | "release": true, 4 | "releaseName": "v${version}" 5 | }, 6 | "git": { 7 | "changelog": "npx auto-changelog --stdout --commit-limit false --unreleased --template https://raw.githubusercontent.com/release-it/release-it/master/templates/changelog-compact.hbs", 8 | "commitMessage": "v${version} release", 9 | "requireCleanWorkingDir": false, 10 | "addUntrackedFiles": false, 11 | "commit": true, 12 | "push": true, 13 | "tagAnnotation": "Release v${version}", 14 | "tagName": "v${version}" 15 | }, 16 | "npm" : { 17 | "publish" : false 18 | }, 19 | "hooks": { 20 | }, 21 | "plugins": { 22 | "@release-it/keep-a-changelog": { 23 | "filename": "CHANGELOG.md", 24 | "strictLatest" : false 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /BingAds-Auth.md: -------------------------------------------------------------------------------- 1 | # BingAds API SDK (Authentication Guide) 2 | 3 | *Note: These steps were tested as of June 3rd, 2021, you may need modify these steps to work for your specific project.* 4 | 5 | This uses the [BingAds-PHP-SDK](https://github.com/BingAds/BingAds-PHP-SDK) for the [Bing Ads API](https://docs.microsoft.com/en-us/bingads/guides/get-started-php?view=bingads-12) 6 | 7 | ## (1) API Access 8 | 9 | You need access before you can use the API, follow the links below to help you gain access. 10 | 11 | [Create Developer Token](https://developers.ads.microsoft.com/Account) 12 | 13 | [Help Guide – Quick Start](https://docs.microsoft.com/en-us/advertising/guides/get-started?view=bingads-13#quick-start-production) 14 | 15 | [Help Guide - Get a Developer Token](https://docs.microsoft.com/en-us/advertising/guides/get-started?view=bingads-13#get-developer-token) 16 | 17 | ## (2) Register Your Application 18 | 19 | Bing Requires you to register your app. This is where you get your client id and client secret. 20 | 21 | 1) [Register Your Application (Azure)](https://go.microsoft.com/fwlink/?linkid=2083908) 22 | 23 | 2) **Supported account types**: select "Accounts in any organizational directory (Any Azure AD directory - Multitenant)" 24 | 25 | 3) Redirect URI: Web type, then add `https://login.microsoftonline.com/common/oauth2/nativeclient` 26 | 27 | 4) Fill in .env `BING_CLIENT_ID` with the "Application (client) ID" from the overview page for your app 28 | 29 | 5) Go to Certificates & secrets and create a new secret 30 | 31 | 6) Copy the "value" of this new secret into the .env `BING_CLIENT_SECRET` (not the secret ID) 32 | 33 | ## (3) Generate Refresh Token 34 | 35 | Run `php artisan laravelads:token:generate --service=BingAds` 36 | 37 | 1) Copy and go to the URL shown after running that command 38 | 39 | 2) It will ask you to login and "approve access" to your application 40 | 41 | 3) You will be redirected to a URL, most likely a white page. COPY the url from in the address bar and paste it into the command line. 42 | 43 | 4) Copy and paste the refresh token you see into .env `BING_REFRESH_TOKEN` 44 | 45 | [Back to Readme](README.md) 46 | 47 | ## Sandbox 48 | 49 | The [Bing Ads API Sandbox](https://docs.microsoft.com/en-us/advertising/guides/sandbox?view=bingads-13) can be used by changing the API environment using the `->setEnvironment('Sandbox')` method. -------------------------------------------------------------------------------- /BingAds-SDK.md: -------------------------------------------------------------------------------- 1 | > Go back to the [README](README.md) for the LaravelAds SDK 2 | 3 | # BingAds - Documentation 4 | 5 | This uses the [BingAds-PHP-SDK](https://github.com/BingAds/BingAds-PHP-SDK) for the [Bing Ads API](https://docs.microsoft.com/en-us/bingads/guides/get-started-php?view=bingads-12). 6 | 7 | ### Getting Started 8 | 9 | First, you need to use the service access for `Bing Ads` and add the Account Id 10 | 11 | ```php 12 | $bingAds = LaravelAds::bingAds()->with('ACCOUNT_ID'); 13 | ``` 14 | 15 | |Method|Description| 16 | |---|---| 17 | |`with(ACCOUNT_ID)`|**(Required)** – This is your "Account Id" (can be found in the url &aid={ YOUR ACCOUNT ID }) 18 | |`withCustomerId(CUSTOMER_ID)`|**(Optional)** – Some requests might require your customer id 19 | 20 | #### Management 21 | * [Fetching - Get Customers](#fetch-customers) 22 | * [Fetching - All Campaigns](#fetch-all-campaigns) 23 | * [Fetching - All Ad Groups](#fetch-all-ad-groups) 24 | * [Management - Campaigns](#campaigns) 25 | * [Management - Ad Groups](#ad-groups) 26 | * [Offline Conversion Import](#offline-conversion-import) 27 | * [Manual Configuration](#manual-configuration) 28 | 29 | #### Reports 30 | * [Account Performance](#account-reports) 31 | * [Campaign Performance](#campaign-reports) 32 | * [Ad Group Performance](#ad-group-reports) 33 | * [Final URL Performance](#final-url-performance-report) 34 | * [Search Term Performance](#search-term-performance-report) 35 | * [Age Range Performance](#age-range-performance-report) 36 | * [Gender Performance](#gender-performance-report) 37 | * [Custom Fields](#custom-fields) 38 | 39 | --------------------------------------------------------- 40 | 41 | ## Fetching 42 | 43 | If you don't want to learn how to handle the BingAds API request, here are **pre-built methods** to quickly get you going. 44 | 45 | ### Fetch Customers 46 | 47 | Fetching all the customers within the account. 48 | 49 | ```php 50 | $customers = $bingAds->fetch()->getCustomers(); 51 | ``` 52 | 53 | *Results: `getCustomers()` (returns a [Laravel Collection](https://laravel.com/docs/collections) object, use `all()` for array)* 54 | 55 | ``` 56 | [0] => Array 57 | ( 58 | [id] => 000000000 59 | [name] => Company Name 60 | ) 61 | ... 62 | ``` 63 | 64 | ### Fetch All Campaigns 65 | 66 | Fetching all campaigns within the account. 67 | 68 | ```php 69 | $campaigns = $bingAds->fetch()->getCampaigns(); 70 | ``` 71 | 72 | *Results: `getCampaigns()` (returns a [Laravel Collection](https://laravel.com/docs/collections) object, use `all()` for array)* 73 | 74 | ``` 75 | [0] => Array 76 | ( 77 | [id] => 000000000 78 | [name] => Campaign Name 79 | [status] => PAUSED 80 | [channel] => SEARCH 81 | [budget] => 5 82 | [bid_strategy] => CPA 83 | ) 84 | ... 85 | ``` 86 | 87 | 88 | ### Fetch All Ad Groups 89 | 90 | Fetching all ad groups within the account. 91 | 92 | ```php 93 | $adgroups = $bingAds->fetch()->getAdGroups(); 94 | ``` 95 | 96 | *Results: `getAdGroups()` (returns a [Laravel Collection](https://laravel.com/docs/collections) object, use `all()` for array)* 97 | 98 | ``` 99 | [0] => Array 100 | ( 101 | [id] => 0000000 102 | [name] => Ad Group Name 103 | [status] => ENABLED 104 | [campaign_id] => 1672496372 105 | [bid_strategy] => CPC 106 | [bid] => 0.65 107 | ) 108 | ... 109 | ``` 110 | 111 | ## Reports 112 | 113 | Here are the **pre-built methods** for retrieving reports. 114 | 115 | ### Account Reports 116 | 117 | Pull **account-level** reports. 118 | 119 | ```php 120 | $accountReport = $bingAds->reports($dateFrom, $dateTo) 121 | ->getAccountReport(); 122 | ``` 123 | 124 | *Results: `getAccountReport()` (returns a [Laravel Collection](https://laravel.com/docs/collections) object, use `all()` for array)* 125 | 126 | ``` 127 | [0] => Array 128 | ( 129 | [date] => 2018-12-26 130 | [account_id] => 0000000 131 | [clicks] => 3131 132 | [impressions] => 73844 133 | [cost] => 1003.90 134 | [conversions] => 206 135 | [conversion_value] => 1039.95 136 | ) 137 | ... 138 | ``` 139 | 140 | 141 | ### Campaign Reports 142 | 143 | Pull **campaign-level** reports. 144 | 145 | ```php 146 | $campaignReport = $bingAds->reports($dateFrom, $dateTo) 147 | ->getCampaignReport(); 148 | ``` 149 | 150 | *Results: `getCampaignReport()` (returns a [Laravel Collection](https://laravel.com/docs/collections) object, use `all()` for array)* 151 | 152 | ``` 153 | [0] => Array 154 | ( 155 | [date] => 2018-12-26 156 | [account_id] => 00000000 157 | [campaign_name] => Campaign Name 158 | [campaign_id] => 00000000 159 | [clicks] => 2 160 | [impressions] => 267 161 | [cost] => 0.53 162 | [conversions] => 0 163 | [conversion_value] => 0.00 164 | ) 165 | ... 166 | ``` 167 | 168 | ### Ad Group Reports 169 | 170 | Pull **adgroup-level** reports. 171 | 172 | ```php 173 | $adgroupReport = $bingAds->reports($dateFrom, $dateTo) 174 | ->getAdGroupReport(); 175 | ``` 176 | 177 | *Results: `getAdGroupReport()` (returns a [Laravel Collection](https://laravel.com/docs/collections) object, use `all()` for array)* 178 | 179 | ``` 180 | [0] => Array 181 | ( 182 | [date] => 2018-12-26 183 | [account_id] => 000000 184 | [campaign_id] => 000000 185 | [campaign_name] => Campaign Name 186 | [ad_group_id] => 000000 187 | [ad_group_name] => Ad Group Name 188 | [clicks] => 684 189 | [impressions] => 6008 190 | [cost] => 290.13 191 | [conversions] => 87 192 | [conversion_value] => 466.05 193 | ) 194 | ... 195 | ``` 196 | 197 | ### Final URL Performance Report 198 | 199 | This report is **campaign-level** and uses [Destination URL Performance](https://docs.microsoft.com/en-us/bingads/reporting-service/destinationurlperformancereportrequest?view=bingads-12). 200 | 201 | ```php 202 | $report = $bingads->reports($dateFrom, $dateTo) 203 | ->getFinalUrlReport(); 204 | ``` 205 | 206 | *Results: `getFinalUrlReport()` (returns a [Laravel Collection](https://laravel.com/docs/collections) object, use `all()` for array)* 207 | 208 | ``` 209 | [0] => Array 210 | ( 211 | [date] => 2018-12-26 212 | [account_name] => Account Name 213 | [account_id] => 00000000 214 | [campaign_id] => 00000000 215 | [campaign_name] => Campaign Name 216 | [clicks] => 684 217 | [impressions] => 6008 218 | [cost] => 290.13 219 | [conversions] => 88 220 | [conversion_value] => 470.80 221 | [destination_url] => (no longer used, look at final url) 222 | [final_url] => https://your-final-url.com/landing-page 223 | ) 224 | ... 225 | ``` 226 | 227 | ### Search Term Performance Report 228 | 229 | This report is aggregated to **account-level** and uses [Search Query Performance](https://docs.microsoft.com/en-us/bingads/reporting-service/searchqueryperformancereportrequest?view=bingads-12). 230 | 231 | ```php 232 | $report = $bingads->reports($dateFrom, $dateTo) 233 | ->getSearchTermReport(); 234 | ``` 235 | 236 | *Results: `getSearchTermReport()` (returns a [Laravel Collection](https://laravel.com/docs/collections) object, use `all()` for array)* 237 | 238 | ``` 239 | // the key of the array is also the search term 240 | // this ensures a unique search term for aggregation 241 | 242 | [food in boston ma] => Array 243 | ( 244 | [search_term] => food in boston ma 245 | [impressions] => 1 246 | [clicks] => 1 247 | [cost] => 0.47 248 | [conversions] => 0.00 249 | [conversion_value] => 0.00 250 | ) 251 | ... 252 | ``` 253 | 254 | ### Age Range Performance Report 255 | 256 | This report is aggregated to **account-level** and uses [Age Gender Performance](https://docs.microsoft.com/en-us/bingads/reporting-service/agegenderaudiencereportcolumn?view=bingads-12). 257 | 258 | ```php 259 | $report = $bingads->reports($dateFrom, $dateTo) 260 | ->getAgeRangeReport(); 261 | ``` 262 | 263 | *Results: `getGenderReport()` (returns a [Laravel Collection](https://laravel.com/docs/collections) object, use `all()` for array)* 264 | 265 | ``` 266 | // the key of the array is also the age range 267 | // this ensures a unique age range for aggregation 268 | // Age (Valid age ranges are 18-24, 25-34, 35-44, 45-54, 55-64, 65 or more, and Unknown.) 269 | 270 | [18-24] => Array 271 | ( 272 | [age_range] => 18-24 273 | [impressions] => 12517 274 | [clicks] => 203 275 | [cost] => 58.65 276 | [conversions] => 11 277 | [conversion_value] => 51.25 278 | ) 279 | ... 280 | ``` 281 | 282 | ### Gender Performance Report 283 | 284 | This report is aggregated to **account-level** and uses [Age Gender Performance](https://docs.microsoft.com/en-us/bingads/reporting-service/agegenderaudiencereportcolumn?view=bingads-12). 285 | 286 | ```php 287 | $report = $bingads->reports($dateFrom, $dateTo) 288 | ->getGenderReport(); 289 | ``` 290 | 291 | *Results: `getGenderReport()` (returns a [Laravel Collection](https://laravel.com/docs/collections) object, use `all()` for array)* 292 | 293 | ``` 294 | // the key of the array is also the gender 295 | // this ensures a unique gender for aggregation 296 | // Genders (Male, Female) 297 | 298 | [Female] => Array 299 | ( 300 | [gender] => Female 301 | [impressions] => 16045 302 | [clicks] => 411 303 | [cost] => 162.28 304 | [conversions] => 18 305 | [conversion_value] => 88 306 | ) 307 | ... 308 | ``` 309 | 310 | ### Custom Fields 311 | 312 | You can set your own fields to pull from Bing Ads. Available fields can be 313 | found in `Microsoft\BingAds\V13\Reporting` 314 | 315 | ```php 316 | use Microsoft\BingAds\V13\Reporting\AdGroupPerformanceReportColumn; 317 | 318 | $adgroupReport = $bingAds->reports($dateFrom, $dateTo) 319 | ->setFields([ 320 | AdGroupPerformanceReportColumn::TimePeriod, 321 | AdGroupPerformanceReportColumn::AccountId, 322 | AdGroupPerformanceReportColumn::CampaignId, 323 | AdGroupPerformanceReportColumn::CampaignName, 324 | AdGroupPerformanceReportColumn::AdGroupId, 325 | AdGroupPerformanceReportColumn::AdGroupName, 326 | AdGroupPerformanceReportColumn::Clicks, 327 | AdGroupPerformanceReportColumn::Impressions, 328 | AdGroupPerformanceReportColumn::Spend, 329 | AdGroupPerformanceReportColumn::Conversions, 330 | // AdGroupPerformanceReportColumn::Revenue, 331 | // AdGroupPerformanceReportColumn::AveragePosition, 332 | 333 | // new custom field 334 | AdGroupPerformanceReportColumn::CostPerConversion, 335 | ]) 336 | ->getAdGroupReport(); 337 | ``` 338 | 339 | *Results: `getAdGroupReport()` (returns a [Laravel Collection](https://laravel.com/docs/collections) object, use `all()` for array)* 340 | 341 | ``` 342 | [0] => Array 343 | ( 344 | [date] => 2018-12-26 345 | [account_id] => 000000 346 | [campaign_id] => 000000 347 | [campaign_name] => Campaign Name 348 | [ad_group_id] => 000000 349 | [ad_group_name] => Ad Group Name 350 | [clicks] => 684 351 | [impressions] => 6008 352 | [cost] => 290.13 353 | [conversions] => 87 354 | [costperconversion] => 3.33 355 | ) 356 | ... 357 | ``` 358 | 359 | 360 | ## Campaigns 361 | 362 | You can easily manage/view campaign settings by loading up the campaign service. 363 | 364 | ```php 365 | $campaign = $bingAds->campaign('CAMPAIGN_ID'); 366 | ``` 367 | 368 | **Available methods** from the `Campaign` object. (the `SET` methods are chainable, see example) 369 | 370 | |Method |Description | 371 | |--- |--- | 372 | |`getId()`|GET the Campaign id| 373 | |`getName()`|GET the Campaign name| 374 | |`getStatus()`|GET the Campaign Status (`ENABLED`,`PAUSED`,`DELETED`)| 375 | |`getBudget()`|GET the Campaign budget| 376 | |`getBudgetDelivery()`|GET the Campaign budget delivery (`ACCELERATED`,`STANDARD`)| 377 | |`getBidStrategy()`|GET the Campaign bid strategy (`CPA`,`CPC`,`ECPC`)| 378 | |`getTargetCpa()`|GET the Campaign Target CPA (if one exists)| 379 | |`setId()`|SET the Campaign id| 380 | |`setName()`|SET the Campaign name| 381 | |`setStatus($status)`|SET Campaign status (`ENABLED`,`PAUSED`)| 382 | |`setBudget($amount)`|SET Campaign Budget (`FLOAT $amount`)| 383 | |`setTargetCpa($amount)`|SET Campaign Target CPA (`FLOAT $amount`)| 384 | |`save()`|Post your changes to the server| 385 | 386 | 387 | ### Example Usage 388 | 389 | ```php 390 | // Rename the campaign, enable it, and then set the budget to $300 391 | $bingAds->campaign('CAMPAIGN_ID') 392 | ->setName('My New Campaign Name') 393 | ->setStatus('ENABLED') 394 | ->setBudget(300) 395 | ->save(); 396 | 397 | // if you only want to grab the campaign name... 398 | $campaignName = $bingAds->campaign('CAMPAIGN_ID')->getName(); 399 | 400 | // Get only the campaign status 401 | $campaignStatus = $bingAds->campaign('CAMPAIGN_ID')->getStatus(); 402 | 403 | ``` 404 | 405 | ## Ad Groups 406 | 407 | You can easily manage/view Ad Group settings by loading up the AdGroup service. 408 | 409 | ```php 410 | // Campaign Id Is Required for Bing Ad Groups. 411 | $adGroup = $bingAds->adGroup('ADGROUP_ID', 'CAMPAIGN_ID'); 412 | ``` 413 | 414 | **Available methods** from the `AdGroup` object. (the `SET` methods are chainable, see example) 415 | 416 | |Method |Description | 417 | |--- |--- | 418 | |`getId()`|GET the AdGroup id| 419 | |`getName()`|GET the AdGroup name| 420 | |`getCampaignId()`|GET the AdGroup Campaign Id| 421 | |`getStatus()`|GET the AdGroup Status (`ENABLED`,`PAUSED`,`DELETED`)| 422 | |`getBidStrategy()`|GET the AdGroup bid strategy (`CPA`,`CPC`,`ECPC`)| 423 | |`getBid()`|GET the AdGroup Bid Amount| 424 | |`setId()`|SET the AdGroup id| 425 | |`setName()`|SET the AdGroup name| 426 | |`setStatus($status)`|SET AdGroup status (`ENABLED`,`PAUSED`)| 427 | |`setBid($amount)`|SET AdGroup Bid (`FLOAT $amount`) *currently only the CPC/ECPC bid*| 428 | |`save()`|Post your changes to the server| 429 | 430 | ### Example Usage 431 | 432 | ```php 433 | // Rename the Ad Group, enable it, and then set the CPC bid to 0.22 434 | $bingAds->adGroup('ADGROUP_ID', 'CAMPAIGN_ID') 435 | ->setName('My New Ad Group Name') 436 | ->setStatus('ENABLED') 437 | ->setBid(0.22) 438 | ->save(); 439 | 440 | // if you only want to grab the ad group name... 441 | $adgroupName = $bingAds->adGroup('ADGROUP_ID', 'CAMPAIGN_ID')->getName(); 442 | 443 | // Get only the ad group status 444 | $adgroupStatus = $bingAds->adGroup('ADGROUP_ID', 'CAMPAIGN_ID')->getStatus(); 445 | 446 | // Get only the ad group bid 447 | $adGroupBid = $bingAds->adGroup('ADGROUP_ID', 'CAMPAIGN_ID')->getBid(); 448 | 449 | ``` 450 | 451 | 452 | ## Offline Conversion Import 453 | 454 | You can import offline conversions using this simple method. Uses [OfflineConversion](https://docs.microsoft.com/en-us/advertising/bulk-service/offline-conversion?view=bingads-13) 455 | 456 | ```php 457 | // You need to pass the customer id for this request 458 | $bingAds->withCustomerId('CUSTOMER_ID'); 459 | 460 | // Can chain and add() as many as you wish 461 | $conversionImport = $bingAds->offlineConversionImport() 462 | ->add([ 463 | 'click_id' => '5de65ff20a9a1957c67c0294d1e9b', 464 | 'value' => 0, 465 | 'name' => 'CONVERSION NAME', 466 | 'time' => 'DATETIME TIMEZONE' 467 | ]) 468 | ->add([ 469 | 'click_id' => 'CjwKCAjwzJjrBRBvEiwA867', 470 | 'value' => 0, 471 | 'name' => 'CONVERSION NAME', 472 | 'time' => 'DATETIME TIMEZONE' 473 | ]); 474 | 475 | // when read, begin the upload 476 | $response = $conversionImport->upload(); 477 | ``` 478 | 479 | *Note: `time` must be in UTC format "2019-08-28T23:11:39.000000Z"* 480 | 481 | **Methods:** 482 | 483 | |Method|Description| 484 | |---|---| 485 | |`add( single array )`|Adding a single conversion 486 | |`addBulk( multi-array )`|Adding an array of single conversions 487 | |`upload()`|Imports the conversions to Bing (pass `true` or `false` as arg to return more detail success array) 488 | 489 | **Response:** 490 | 491 | The array response is both `success` and `errors`, errors will include [Bing Error Codes](https://docs.microsoft.com/en-us/advertising/guides/operation-error-codes?view=bingads-13#5500) 492 | 493 | *Note: Click Ids that are success will not appear in the errors array and vise-versa.* 494 | 495 | ``` 496 | Array 497 | ( 498 | [errors] => Array 499 | ( 500 | [0] => Array 501 | ( 502 | [click_id] => CjwKCAjwzJjrBRBvEiwA867 503 | [error] => OfflineConversionMicrosoftClickIdInvalid 504 | ) 505 | ) 506 | [success] => Array 507 | ( 508 | [1] => 5de65ff20a9a1957c67c0294d1e9b 509 | ) 510 | ) 511 | ... 512 | 513 | ``` 514 | 515 | 516 | ## Manual Configuration 517 | 518 | By default, the configuration will always look at the `/config/bing-ads.php`, however, you can override that by injecting your own config into the bing ads service object. 519 | 520 | **You only need to use this if you WANT to override the config, otherwise the config file will work in most cases.** 521 | 522 | ```php 523 | $bingAds = LaravelAds::bingAds(); 524 | $bingAds->configuration([ 525 | 'developerToken' => '', 526 | 'clientId' => '', 527 | 'clientSecret' => '', 528 | 'refreshToken' => '' 529 | ]); 530 | 531 | $bingAds = $bingAds->with('ACCOUNT_ID'); 532 | 533 | // after the config is set above, now you can use the SDK as you normally do... 534 | // $report = $bingAds->reports('2020-01-01', '2020-01-05')->getAccountReport(); 535 | 536 | ``` 537 | 538 | 539 | ## Jump To: 540 | * [Home](README.md) 541 | * [GoogleAds - Getting Started](GoogleAds-SDK.md) 542 | * [BingAds - Getting Started](BingAds-SDK.md) 543 | * [FacebookAds - Getting Started](FacebookAds-SDK.md) -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Change Log 2 | ========== 3 | 4 | ## [1.5.0] - 2022-04-08 5 | 6 | ### Changed 7 | * Bump minimist from 1.2.5 to 1.2.6 #46 8 | * Updated facebook php sdk to use v13 instead of v12 #49 9 | 10 | ### Fixed 11 | * Bing ads random report-id name extension #47 12 | 13 | ------------------------------------------------------- 14 | 15 | ## [1.4.0] - 2021-10-26 16 | 17 | ### Added 18 | * Added Macroable trait to Service classes for extensibility [Readme](README.md#Customization) 19 | 20 | ### Changed 21 | * Updated facebook php sdk to use v12 instead of v11 22 | 23 | ------------------------------------------------------- 24 | 25 | ## [1.3.0] - 2021-07-25 26 | 27 | ### Added 28 | * New facebook integration [Readme](FacebookAds-SDK.md#facebookads---documentation) 29 | * New facebook API v11 using [Marketing API](https://developers.facebook.com/docs/marketing-apis) and [facebook-php-business-sdk](https://github.com/facebook/facebook-php-business-sdk) 30 | * Facebook: Fetch Campaigns and AdSets 31 | * Facebook: Insight reports for Account, Campaign and AdSet performance 32 | 33 | ------------------------------------------------------- 34 | 35 | ## [1.2.23] - 2021-06-08 36 | 37 | ### Fixed 38 | * Fixed bing offline conversion response by changing `click` to `click_id` and adding `name` and `time` to match Google 39 | 40 | ------------------------------------------------------- 41 | 42 | ## [1.2.22] - 2021-06-03 43 | 44 | ### Added 45 | * Added ability to specify fields for Bing Ads reports 46 | * Added getCustomers() endpoint for Bing Ads 47 | 48 | ### Changed 49 | * Made the redirect_uri config setting an env variable 50 | * Updated the laravel vendor:publish to show laravel-ads-sdk instead of config 51 | 52 | ### Fixed 53 | * Url encoded spaces in OAuth Grant URL 54 | 55 | ------------------------------------------------------- 56 | 57 | ## [1.2.21] - 03-31-2021 58 | 59 | ### Added 60 | * For the google/bing offline conversions import and with setting of output value, now returns name and time. 61 | 62 | ### Fixed 63 | * Fixed offline conversions for both google/bing to return the proper click id on errors (was returning the same click id for all errors) 64 | 65 | ------------------------------------------------------- 66 | 67 | ## [1.2.20] - 03-30-2021 68 | 69 | ### Added 70 | * Added a way to output the conversion value of success/error offline conversion imports for google/bing ads 71 | 72 | ------------------------------------------------------- 73 | 74 | ## [1.2.19] - 12-27-2020 75 | 76 | ### Changed 77 | * Updated composer.json Google Ads API to v49 based on #19 (no other AdWords changes were made) 78 | 79 | ------------------------------------------------------- 80 | 81 | ## [1.2.18] - 06-02-2020 82 | 83 | ### Changed 84 | * Updated Google Ads API to v47 (from v40) to fix the dependency issue #10 85 | 86 | ------------------------------------------------------- 87 | 88 | ## [1.2.17] - 04-12-2020 89 | 90 | ------------------------------------------------------- 91 | 92 | ### Changed 93 | * Google Ads `getTargetCpa()` suppressing any null errors when trying to get the target cpa value. 94 | 95 | ------------------------------------------------------- 96 | 97 | ## [1.2.16] - 03-30-2020 98 | 99 | ### Added 100 | * Ability to set the configuration manaually (overriding the laravel config settings) by using `configuration(array)` 101 | 102 | ------------------------------------------------------- 103 | 104 | ## [1.2.15] - 12-13-2019 105 | 106 | ### Changed 107 | * Cleaning up more `BingAds` for Soap errors due to undefined variables. (raw output of errors). 108 | 109 | ------------------------------------------------------- 110 | 111 | ## [1.2.14] - 12-12-2019 112 | 113 | ### Changed 114 | * Cleaned up `BingAds` for Soap errors due to request of `__getLastRequest()` undefined errors. 115 | 116 | ------------------------------------------------------- 117 | 118 | ## [1.2.13] - 11-07-2019 119 | 120 | ### Added 121 | * Added `InvalidCredentials` Error code with Offline Conversion Import on `BingAds` 122 | * Added `ApiVersionNoLongerSupported` Error code with Offline Conversion Import on `BingAds` 123 | * Added `withRedirectUri()` by default for `BingAds` Auth 124 | * Added config variable `redirect_uri` by default it will use `https://login.microsoftonline.com/common/oauth2/nativeclient` 125 | 126 | ### Changed 127 | * Fixed `BingAds` now using `^0.12` instead of deprecated `v0.11` as of 11/6 (version of SDK) 128 | * Changed `BingAds` Services to use `V13` namespace instead of `V12` (version of API) 129 | 130 | ------------------------------------------------------- 131 | 132 | ## [1.2.12] - 03-29-2019 133 | 134 | ### Added 135 | * Added Offline Conversion Import services for both `BingAds` and `GoogleAds`. 136 | * Added `withCustomerId()` to the `BingAds` service as some requests require it. 137 | 138 | ------------------------------------------------------- 139 | 140 | ## [1.2.11] - 03-29-2019 141 | 142 | ### Changed 143 | * Fixed BingAds from running too many rows for Search Terms (using Summary instead of Daily feed) 144 | * Updated BingAds to increase time checking for API reports 145 | * Updated BingAds Report Names and Aggregation to choose, daily, hourly, summary on account level. 146 | * Updated GoogleAds API to version 40 147 | 148 | ------------------------------------------------------- 149 | 150 | ## [1.2.10] - 03-04-2019 151 | 152 | ### Changed 153 | * Fixed GoogleAds Reports by bad parsing of csv data. Due to comma issues. Now using `str_getcsv` 154 | * GoogleAds Report: Added default columns to rows (based on headers) if the column was missing 155 | 156 | ------------------------------------------------------- 157 | 158 | ## [1.2.9] - 02-26-2019 159 | 160 | ### Added 161 | * Added `campaign_name` to the resposne of `getAdGroupReport()`. 162 | * Added Reports: `getFinalUrlReport()` on both Google/Bing (it returns per campaign) 163 | * Added Reports: `getPlacementReport()` on Google only 164 | * Added Reports: `getPlacementUrlReport()` on Google only 165 | * Added Reports: `getSearchTermReport()` on Google/Bing 166 | * Added Reports: `getAgeRangeReport()` on Google/Bing 167 | * Added Reports: `getGenderReport()` on Google/Bing 168 | 169 | ------------------------------------------------------- 170 | 171 | ## [1.2.8] - 02-22-2019 172 | 173 | ### Changed 174 | * Fixed fetching `getAdGroups()` on Bing causing undefined error when AdGroups not returned. 175 | 176 | ------------------------------------------------------- 177 | 178 | ## [1.2.7] - 02-02-2019 179 | 180 | ### Changed 181 | * Added `getCampaigns()` and `getAdGroups()` to now work with paging. Google allows only 10,000 results per page. Pre-defined 5,000 per page in this SDK for safety. It will be handled automatically and give you the entire total in the response. 182 | * Added `$filters` array within the `getCampaigns()` and `getAdGroups()`, using the `Predicate` and `setPredicates` Google Ads functionality. (Note: this currently will only use the `PredicateOperator::IN` operator.) 183 | 184 | ------------------------------------------------------- 185 | 186 | ## [1.2.6] - 02-01-2019 187 | 188 | ### Changed 189 | * Updated GoogleAds API from `v201802` to `v201809` 190 | 191 | ------------------------------------------------------- 192 | 193 | ## [1.2.5] - 01-25-2019 194 | 195 | ### Changed 196 | * GoogleAds Reports now allow for advanced customization, making `reportDownload()` a public method. 197 | * GoogleAds Reports now allow `aggregate()` to be used with multiple calls. 198 | 199 | ------------------------------------------------------- 200 | 201 | ## [1.2.4] - 01-22-2019 202 | 203 | ### Added 204 | * GoogleAds: `AveragePosition` which equals `avg_position` within AdGroup Report `getAdGroupReport`. 205 | * BingAds: `AveragePosition` which equals `avg_position` within AdGroup Report `getAdGroupReport()`. 206 | 207 | ------------------------------------------------------- 208 | 209 | ## [1.2.3] - 01-18-2019 210 | 211 | ### Added 212 | * `setTargetCpa()` on Bing/Google campaigns. 213 | 214 | ------------------------------------------------------- 215 | 216 | ## [1.2.2] - 01-17-2019 217 | 218 | ### Changed 219 | * Fixed bing report response once again! 220 | 221 | ------------------------------------------------------- 222 | 223 | ## [1.2.1] - 01-17-2019 224 | 225 | ### Changed 226 | * Fixed Bing Report Download when download fails due to "no data in report" (now returns a empty response) 227 | 228 | ------------------------------------------------------- 229 | 230 | ## [1.2.0] - 01-17-2019 231 | 232 | * Overhaul of Google and Bing APIs 233 | * Includes Google/Bing Campaign and AdGroup Management Features. 234 | * Updated Readme with all Documentation for both Google/Bing 235 | * Improved consistency between both Google/Bing 236 | 237 | ------------------------------------------------------- 238 | 239 | ## [1.1.6] - 01-14-2019 240 | 241 | ### Fixed 242 | * Fixed new bid type method returns the correct types now. 243 | 244 | ------------------------------------------------------- 245 | 246 | ## [1.1.5] - 01-14-2019 247 | 248 | ### Fixed 249 | * Added `Campaign Id` into Bing Ad Group response. 250 | 251 | ------------------------------------------------------- 252 | 253 | ## [1.1.4] - 01-14-2019 254 | 255 | ### Changed 256 | * Changed Bing Ad Group status from `Active` to `ENABLED` (matching GoogleAds) 257 | * Fetching AdGroups now use the AdGroup Response object. 258 | 259 | ------------------------------------------------------- 260 | 261 | ## [1.1.3] - 01-14-2019 262 | 263 | ### Added 264 | * Google: Added `type` for `AdGroupType` in `getAdGroups()` 265 | 266 | ### Changed 267 | * Changed the Bid Type to always return a uniform set (`CPC`,`ECPC`,`CPA`,`CPM`) 268 | 269 | ------------------------------------------------------- 270 | 271 | ## [1.1.2] - 01-14-2019 272 | 273 | ### Added 274 | * Google: Ability to check if an ad group is Enhanced CPC using `isEnhancedCpc()` 275 | * Google: Added `ENHANCED_CPC` for bid strategy response in fetch ad groups 276 | 277 | ------------------------------------------------------- 278 | 279 | ## [1.1.1] - 01-05-2019 280 | 281 | ### Added 282 | * For Google/Bing get the active bid with `getBid()` on adgroups. 283 | 284 | ------------------------------------------------------- 285 | 286 | ## [1.1.0] - 01-05-2019 287 | 288 | ### Changed 289 | * Response of `adGroup` for both Google/Bing 290 | * Request to the servers will use `AdGroupRequest` 291 | * Response from the servers will use `AdGroupResponse` 292 | * Bing now has edit ability (and uses the same methods as Google) 293 | * Added new Set/Get Methods to Google/Bing 294 | 295 | ------------------------------------------------------- 296 | 297 | ## [1.0.7] - 12-30-2018 298 | 299 | ### Added 300 | * Added `toCollection()` on `ReportDownload`, instead of sending back raw array, we will send back as a Collection. You can use `all()` if you want the basic array response. 301 | 302 | ### Changed 303 | * The default `ReportDownload` response to `toCollection()` instead of `toArray()` 304 | 305 | ------------------------------------------------------- 306 | 307 | ## [1.0.6] - 12-29-2018 308 | 309 | ### Added 310 | * Console `RefreshTokenCommand`, you can now generate a refresh token and follow authentication steps for BingAds 311 | * Command: `php artisan laravelads:token:generate --service=BingAds` 312 | * BingAds: Added new fetch methods `getCampaigns()` and `getAdGroups()` 313 | * GoogleAds: Added `bid_type` on `getCampaigns()` 314 | 315 | ### Changed 316 | * Simplified the GoogleAds/BingAds Reports and made the responses consistent with both. 317 | 318 | ------------------------------------------------------- 319 | 320 | ## [1.0.5] - 12-29-2018 321 | 322 | ### Added 323 | * Console `RefreshTokenCommand`, you can now generate a refresh token and follow authentication steps for GoogleAds 324 | * Command: `php artisan laravelads:token:generate --service=GoogleAds` 325 | 326 | ------------------------------------------------------- 327 | 328 | ## [1.0.4] - 12-29-2018 329 | 330 | ### Changed 331 | * Services: `googleAds()`, `bingAds()` and `facebookAds()` methods. 332 | 333 | ------------------------------------------------------- 334 | 335 | ## [1.0.3] - 12-28-2018 336 | 337 | ### Added 338 | * GoogleAds: `AdGroupOperation` new method `getBidType()`. 339 | 340 | ### Fixed 341 | * GoogleAds: `AdGroupOperation` updated pull from the server when loaded. 342 | 343 | ------------------------------------------------------- 344 | 345 | ## [1.0.2] - 12-28-2018 346 | 347 | ### Added 348 | * GoogleAds: `AdGroupOperation` with ability to change adgroup bids using `setBid()` 349 | * GoogleAds: `AdGroupOperation` ability to change name and status of adgroups. 350 | * GoogleAds: `AdGroupOperation` with a `save()` method that posts changes to Google Ads. 351 | * GoogleAds: `AdGroupOperation` Added getter methods `getName()`, `getStatus()`, `getCampaignId()`, `getId()` 352 | 353 | ------------------------------------------------------- 354 | 355 | ## [1.0.1] - 12-28-2018 356 | 357 | ### Added 358 | * Bing Reports (Account, Campaign and Ad Group Level) 359 | * Bing Authentication Session and basic Bing service methods 360 | 361 | ------------------------------------------------------- 362 | 363 | ## [1.0.0] - 12-27-2018 364 | * Initial Release for `Google Ads` 365 | -------------------------------------------------------------------------------- /FacebookAds-Auth.md: -------------------------------------------------------------------------------- 1 | # Facebook Ads SDK (Authentication Guide) 2 | 3 | This uses the [facebook-php-business-sdk](https://github.com/facebook/facebook-php-business-sdk) for [Facebook Marketing API](https://developers.facebook.com/docs/marketing-apis). 4 | 5 | ## Guide to gain API Access 6 | 7 | 1) Create an app at [developers.facebook.com](https://developers.facebook.com/) 8 | 9 | 2) Copy the App Id and paste it into env `FB_APP_ID` 10 | 11 | 3) On the menu, go to settings and get "App Secret" copy and paste this into the env `FB_APP_SECRET` 12 | 13 | 4) Add the product Marketing API to your app, selecting permissions you would like to grant the app and click "Get Token". 14 | 15 | 5) Copy and paste the token shown into the env `FB_ACCESS_TOKEN` 16 | 17 | [Back to Readme](README.md#for-facebookads) 18 | 19 | 20 | ### External Resources: 21 | 22 | - [Quick Start Guide](https://github.com/facebook/facebook-php-business-sdk?fbclid=IwAR30iukDiegrACx9BJnbhJKIU2X3SaayDAr_YkbQzXU6C_96PFd-27mj5kc#quick-start) 23 | - [Access Tokens](https://developers.facebook.com/docs/facebook-login/access-tokens) -------------------------------------------------------------------------------- /FacebookAds-SDK.md: -------------------------------------------------------------------------------- 1 | > Go back to the [README](README.md) for the LaravelAds SDK 2 | 3 | # FacebookAds - Documentation 4 | 5 | This uses the [facebook-php-business-sdk](https://github.com/facebook/facebook-php-business-sdk) for [Facebook Marketing API](https://developers.facebook.com/docs/marketing-apis). 6 | 7 | ### :rocket: Getting Started 8 | 9 | First, you'll need the ads account id, this can be found within your ads manager. You can also locate this in the facebook ads manager url `act=ACCOUNT_ID`. 10 | 11 | ```php 12 | $facebookAds = LaravelAds::facebookAds()->with('ADS_ACCOUNT_ID'); 13 | ``` 14 | 15 | #### Management 16 | * [Fetching - Campaigns](#white_check_mark-fetch-campaigns) 17 | * [Fetching - Ad Groups](#white_check_mark-fetch-ad-groups) 18 | #### Reports 19 | * [Account Performance](#white_check_mark-account-reports) 20 | * [Campaign Performance](#white_check_mark-campaign-reports) 21 | * [Ad Group Performance](#white_check_mark-ad-group-reports) 22 | 23 | --------------------------------------------------------- 24 | 25 | ## :rocket: Fetching 26 | 27 | If you don't want to learn how to handle the Facebook Ads API request, here are **pre-built methods** to quickly get you going. 28 | 29 | ### :white_check_mark: Fetch Campaigns 30 | 31 | Fetching all campaigns within the account. 32 | 33 | ```php 34 | $campaigns = $facebookAds->fetch()->getCampaigns(); 35 | ``` 36 | 37 | *Results: `getCampaigns()` (returns a [Laravel Collection](https://laravel.com/docs/collections) object, use `all()` for array)* 38 | 39 | ``` 40 | [0] => Array 41 | ( 42 | [account_id] => 000000000000000000 43 | [id] => 000000000000000000 44 | [name] => Campaign Name 45 | [status] => PAUSED 46 | ) 47 | ... 48 | ``` 49 | 50 | #### Choosing Fields 51 | 52 | You can also choose which fields (columns) you wish to return, `getCampaigns(Array $fields)`. Here are the [Allowed Fields](https://github.com/facebook/facebook-php-business-sdk/blob/master/src/FacebookAds/Object/Fields/CampaignFields.php) 53 | 54 | ```php 55 | use FacebookAds\Object\Fields\CampaignFields; 56 | 57 | // This is the default field list 58 | // https://github.com/facebook/facebook-php-business-sdk/blob/master/src/FacebookAds/Object/Fields/CampaignFields.php 59 | $fields = [ 60 | CampaignFields::ACCOUNT_ID, 61 | CampaignFields::ID, 62 | CampaignFields::NAME, 63 | CampaignFields::STATUS, 64 | CampaignFields::BID_STRATEGY, 65 | CampaignFields::DAILY_BUDGET 66 | ]; 67 | 68 | // (OPTIONAL) 69 | // You can pass in params to filter the response 70 | // Filter by status: this will get all PAUSED campaigns 71 | // https://developers.facebook.com/docs/marketing-api/reference/ad-account/campaigns/ 72 | $params = ['effective_status'=>['PAUSED']]; 73 | 74 | $campaigns = $facebookAds->fetch()->getCampaigns($fields, $params); 75 | 76 | ``` 77 | 78 | ### :white_check_mark: Fetch Ad Groups 79 | 80 | Fetching all ad groups (ad sets) within the account. You can also use `->getAdGroups()`, will be the same as example below. 81 | 82 | ```php 83 | $adgroups = $facebookAds->fetch()->getAdSets(); 84 | ``` 85 | 86 | *Results: `getAdSets()` (returns a [Laravel Collection](https://laravel.com/docs/collections) object, use `all()` for array)* 87 | 88 | ``` 89 | [0] => Array 90 | ( 91 | [account_id] => 000000000000000000 92 | [campaign_id] => 000000000000000000 93 | [id] => 000000000000000000 94 | [name] => Ad Set Name 95 | [status] => ACTIVE 96 | [daily_budget] => 2000 97 | [bid_amount] => 140 98 | [bid_strategy] => LOWEST_COST_WITH_BID_CAP 99 | ) 100 | ... 101 | ``` 102 | 103 | #### Choosing Fields 104 | 105 | You can also choose which fields (columns) you wish to return, `getAdSets(Array $fields)`. Here are the [Allowed Fields](https://github.com/facebook/facebook-php-business-sdk/blob/master/src/FacebookAds/Object/Fields/AdSetFields.php) 106 | 107 | ```php 108 | use FacebookAds\Object\Fields\AdSetFields; 109 | 110 | // This is the default field list 111 | // https://github.com/facebook/facebook-php-business-sdk/blob/master/src/FacebookAds/Object/Fields/AdSetFields.php 112 | $fields = [ 113 | AdSetFields::ACCOUNT_ID, 114 | AdSetFields::CAMPAIGN_ID, 115 | AdSetFields::ID, 116 | AdSetFields::NAME, 117 | AdSetFields::STATUS, 118 | AdSetFields::DAILY_BUDGET, 119 | AdSetFields::BID_AMOUNT, 120 | AdSetFields::BID_STRATEGY 121 | ]; 122 | 123 | // (OPTIONAL) 124 | // You can pass in params to filter the response 125 | // Filter by status: this will get all PAUSED campaigns 126 | // https://developers.facebook.com/docs/marketing-api/reference/ad-account/adsets/ 127 | $params = ['effective_status'=>['PAUSED']]; 128 | 129 | $adgroups = $facebookAds->fetch()->getAdSets($fields, $params); 130 | 131 | ``` 132 | 133 | ## :rocket: Reports 134 | 135 | Here are the **pre-built methods** for retrieving reports. 136 | 137 | ### :white_check_mark: Account Reports 138 | 139 | Pull **Account** reports. This report uses [Insights API](https://developers.facebook.com/docs/marketing-api/insights). 140 | 141 | ```php 142 | $accountReport = $facebook->reports($dateFrom, $dateTo) 143 | ->getAccountReport(); 144 | ``` 145 | 146 | *Results: `getAccountReport()` (returns a [Laravel Collection](https://laravel.com/docs/collections) object, use `all()` for array)* 147 | 148 | ``` 149 | [0] => Array 150 | ( 151 | [account_id] => 0000000000 152 | [date] => 2018-12-16 153 | [impressions] => 3286 154 | [clicks] => 294 155 | [cost] => 724 156 | [conversions] => 9.00 157 | [conversion_value] => 15.75 158 | ) 159 | ... 160 | ``` 161 | 162 | ### :white_check_mark: Campaign Reports 163 | 164 | Pull **Campaign** reports. This report uses [Insights API](https://developers.facebook.com/docs/marketing-api/insights). 165 | 166 | ```php 167 | $campaignReport = $facebookAds->reports($dateFrom, $dateTo) 168 | ->getCampaignReport(); 169 | ``` 170 | 171 | *Results: `getCampaignReport()` (returns a [Laravel Collection](https://laravel.com/docs/collections) object, use `all()` for array)* 172 | 173 | This is the response by default: you can alter the fields by `->setFields(Array)` and `setParams(Array)` [Fields and Parameters](https://developers.facebook.com/docs/marketing-api/insights/parameters/v11.0) 174 | 175 | ``` 176 | [0] => Array 177 | ( 178 | [account_id] => 0000000000 179 | [date] => 2018-12-15 180 | [campaign_name] => Campaign Name 181 | [campaign_id] => 000000 182 | [impressions] => 2 183 | [clicks] => 0 184 | [cost] => 0 185 | [conversions] => 0.00 186 | [conversion_value] => 0.00 187 | ) 188 | ... 189 | ``` 190 | 191 | ### :white_check_mark: Ad Group Reports 192 | 193 | Pull **Ad Set** reports. This report uses [Insights API](https://developers.facebook.com/docs/marketing-api/insights). 194 | 195 | ```php 196 | $adsetReport = $facebookAds->reports($dateFrom, $dateTo) 197 | ->getAdSetReport(); 198 | ``` 199 | 200 | *Results: `getAdSetReport()` (returns a [Laravel Collection](https://laravel.com/docs/collections) object, use `all()` for array)* 201 | 202 | This is the response by default: you can alter the fields by `->setFields(Array)` and `setParams(Array)` [Fields and Parameters](https://developers.facebook.com/docs/marketing-api/insights/parameters/v11.0) 203 | 204 | ``` 205 | [0] => Array 206 | ( 207 | [account_id] => 0000000000 208 | [date] => 2021-12-18 209 | [adset_id] => 000000 210 | [adset_name] => Ad Set Name 211 | [campaign_id] => 0000000 212 | [campaign_name] => Campaign Name 213 | [impressions] => 2 214 | [clicks] => 0 215 | [cost] => 0 216 | [conversions] => 0.00 217 | [conversion_value] => 0.00 218 | ) 219 | ... 220 | ``` 221 | 222 | ### :white_check_mark: Choosing Fields for Insight Reports 223 | 224 | You can also choose which fields you wish to return, `setFields(Array $fields)`. Here are the [Allowed Fields](https://github.com/facebook/facebook-php-business-sdk/blob/master/src/FacebookAds/Object/Fields/AdsInsightsFields.php) 225 | 226 | ```php 227 | use FacebookAds\Object\Fields\AdsInsightsFields; 228 | 229 | // This is the default field list 230 | // https://github.com/facebook/facebook-php-business-sdk/blob/master/src/FacebookAds/Object/Fields/AdsInsightsFields.php 231 | $fields = [ 232 | AdsInsightsFields::ACCOUNT_ID, 233 | AdsInsightsFields::CAMPAIGN_ID, 234 | AdsInsightsFields::CAMPAIGN_NAME, 235 | AdsInsightsFields::IMPRESSIONS, 236 | AdsInsightsFields::CLICKS, 237 | AdsInsightsFields::CTR, 238 | AdsInsightsFields::CONVERSIONS, 239 | AdsInsightsFields::SPEND 240 | ]; 241 | 242 | $report = $facebookAds->reports($dateFrom, $dateTo) 243 | ->setFields($fields)->getCampaignReport() 244 | 245 | ``` 246 | 247 | ## Jump To: 248 | * [Home](README.md) 249 | * [GoogleAds - Getting Started](GoogleAds-SDK.md) 250 | * [BingAds - Getting Started](BingAds-SDK.md) 251 | * [FacebookAds - Getting Started](FacebookAds-SDK.md) 252 | -------------------------------------------------------------------------------- /GoogleAds-Auth.md: -------------------------------------------------------------------------------- 1 | # GoogleAds API SDK (Authentication Guide) 2 | 3 | This uses the [googleads-php-lib](https://github.com/googleads/googleads-php-lib) SDK for the [Google Ads API](https://developers.google.com/adwords/api/docs/guides/start). 4 | 5 | ## (1) API Access 6 | 7 | You need access before you can use the Google Ads API. You can also setup a [test account](https://developers.google.com/google-ads/api/docs/first-call/overview#test_account) for sandbox mode. 8 | 9 | [Request Google Ads API Access](https://services.google.com/fb/forms/newtoken/) 10 | 11 | **Developer tokens** – are located in the API Center within your Google Ads account. Go to "Tools" -> "API Center" 12 | 13 | ## (2) Get Your API Client/Secret keys 14 | 15 | Create an application using the [Google Cloud Platform](https://console.cloud.google.com) 16 | 17 | 1) Select your project/application 18 | 19 | 2) Enable Google Ads API 20 | 21 | 3) "Create Credentials" for OAuth client (fill in name, email and add in Google Ads API as the scope) 22 | 23 | 4) Create OAuth Client ID (select **Application type: Desktop App or Native App**) 24 | 25 | 5) Fill in .env `ADWORDS_OAUTH2_CLIENT_ID` and `ADWORDS_OAUTH2_CLIENT_SECRET` 26 | 27 | ## (3) Generate Refresh Token 28 | 29 | Run `php artisan laravelads:token:generate --service=GoogleAds` 30 | 31 | 1) Copy and go to the URL shown after running that command 32 | 33 | 2) It will ask you to select/login to your Google Ads Account 34 | 35 | 3) Select "Allow" access to your application 36 | 37 | 4) Copy the code you see, and paste it into the command line; then copy and paste the refresh token you see into .env `ADWORDS_OAUTH2_REFRESH_TOKEN` 38 | 39 | [Back to Readme](README.md) 40 | -------------------------------------------------------------------------------- /GoogleAds-SDK.md: -------------------------------------------------------------------------------- 1 | > Go back to the [README](README.md) for the LaravelAds SDK 2 | 3 | # GoogleAds - Documentation 4 | 5 | This uses the [googleads-php-lib](https://github.com/googleads/googleads-php-lib) SDK for the [Google Ads API](https://developers.google.com/adwords/api/docs/guides/start). 6 | 7 | ### Getting Started 8 | 9 | First, you need to use the service access for `Google Ads` and add the Client Customer Id 10 | 11 | ```php 12 | $googleAds = LaravelAds::googleAds()->with('CLIENT_ID'); 13 | ``` 14 | 15 | #### Management 16 | * [Fetching - All Campaigns](#fetch-all-campaigns) 17 | * [Fetching - All Ad Groups](#fetch-all-ad-groups) 18 | * [Management - Campaigns](#campaigns) 19 | * [Management - Ad Groups](#ad-groups) 20 | * [Offline Conversion Import](#offline-conversion-import) 21 | * [Manual Configuration](#manual-configuration) 22 | * [Advanced Options](#need-more-advanced-options) 23 | 24 | #### Reports 25 | * [Account Performance](#account-reports) 26 | * [Campaign Performance](#campaign-reports) 27 | * [Ad Group Performance](#ad-group-reports) 28 | * [Final URL Performance](#final-url-performance-report) 29 | * [Placement Domain Performance](#placement-domain-performance-report) 30 | * [Placement URL Performance](#placement-url-performance-report) 31 | * [Search Term Performance](#search-term-performance-report) 32 | * [Age Range Performance](#age-range-performance-report) 33 | * [Gender Performance](#gender-performance-report) 34 | 35 | --------------------------------------------------------- 36 | 37 | ## Fetching 38 | 39 | If you don't want to learn how to handle the Google Ads API request, here are **pre-built methods** to quickly get you going. 40 | 41 | ### Fetch All Campaigns 42 | 43 | Fetching all campaigns within the account. 44 | 45 | ```php 46 | $campaigns = $googleAds->fetch()->getCampaigns(); 47 | ``` 48 | 49 | *Results: `getCampaigns()` (returns a [Laravel Collection](https://laravel.com/docs/collections) object, use `all()` for array)* 50 | 51 | ``` 52 | [0] => Array 53 | ( 54 | [id] => 000000000 55 | [name] => Campaign Name 56 | [status] => PAUSED 57 | [channel] => SEARCH 58 | [budget] => 5 59 | [bid_strategy] => CPA 60 | ) 61 | ... 62 | ``` 63 | 64 | 65 | ### Fetch All Ad Groups 66 | 67 | Fetching all ad groups within the account. 68 | 69 | ```php 70 | $adgroups = $googleAds->fetch()->getAdGroups(); 71 | ``` 72 | 73 | *Results: `getAdGroups()` (returns a [Laravel Collection](https://laravel.com/docs/collections) object, use `all()` for array)* 74 | 75 | ``` 76 | [0] => Array 77 | ( 78 | [id] => 0000000 79 | [name] => Ad Group Name 80 | [status] => ENABLED 81 | [campaign_id] => 1672496372 82 | [type] => SEARCH_STANDARD 83 | [bid_strategy] => CPC 84 | [bid] => 0.65 85 | ) 86 | ... 87 | ``` 88 | 89 | ## Reports 90 | 91 | Here are the **pre-built methods** for retrieving reports. 92 | 93 | ### Account Reports 94 | 95 | Pull **account-level** reports. This report uses [Account Performance](https://developers.google.com/adwords/api/docs/appendix/reports/account-performance-report). 96 | 97 | ```php 98 | $accountReport = $googleAds->reports($dateFrom, $dateTo) 99 | ->getAccountReport(); 100 | ``` 101 | 102 | *Results: `getAccountReport()` (returns a [Laravel Collection](https://laravel.com/docs/collections) object, use `all()` for array)* 103 | 104 | ``` 105 | [0] => Array 106 | ( 107 | [date] => 2018-12-16 108 | [impressions] => 3286 109 | [clicks] => 294 110 | [cost] => 724 111 | [conversions] => 9.00 112 | [conversion_value] => 15.75 113 | ) 114 | ... 115 | ``` 116 | 117 | 118 | ### Campaign Reports 119 | 120 | Pull **campaign-level** reports. This report uses [Campaign Performance](https://developers.google.com/adwords/api/docs/appendix/reports/campaign-performance-report). 121 | 122 | ```php 123 | $campaignReport = $googleAds->reports($dateFrom, $dateTo) 124 | ->getCampaignReport(); 125 | ``` 126 | 127 | *Results: `getCampaignReport()` (returns a [Laravel Collection](https://laravel.com/docs/collections) object, use `all()` for array)* 128 | 129 | ``` 130 | [0] => Array 131 | ( 132 | [date] => 2018-12-15 133 | [channel] => Search 134 | [campaign_status] => paused 135 | [campaign_name] => Campaign Name 136 | [campaign_id] => 000000 137 | [impressions] => 2 138 | [clicks] => 0 139 | [cost] => 0 140 | [conversions] => 0.00 141 | [conversion_value] => 0.00 142 | ) 143 | ... 144 | ``` 145 | 146 | ### Ad Group Reports 147 | 148 | Pull **adgroup-level** reports. This report uses [AdGroup Performance](https://developers.google.com/adwords/api/docs/appendix/reports/adgroup-performance-report). 149 | 150 | ```php 151 | $adgroupReport = $googleAds->reports($dateFrom, $dateTo) 152 | ->getAdGroupReport(); 153 | ``` 154 | 155 | *Results: `getAdGroupReport()` (returns a [Laravel Collection](https://laravel.com/docs/collections) object, use `all()` for array)* 156 | 157 | ``` 158 | [0] => Array 159 | ( 160 | [date] => 2018-12-18 161 | [ad_group_id] => 000000 162 | [ad_group_name] => Ad Group Name 163 | [campaign_id] => 0000000 164 | [campaign_name] => Campaign Name 165 | [impressions] => 2 166 | [clicks] => 0 167 | [cost] => 0 168 | [conversions] => 0.00 169 | [conversion_value] => 0.00 170 | ) 171 | ... 172 | ``` 173 | 174 | ### Final URL Performance Report 175 | 176 | This report is **campaign-level** and uses [Final URL Performance](https://developers.google.com/adwords/api/docs/appendix/reports/final-url-report). 177 | 178 | ```php 179 | $report = $googleAds->reports($dateFrom, $dateTo) 180 | ->getFinalUrlReport(); 181 | ``` 182 | 183 | *Results: `getFinalUrlReport()` (returns a [Laravel Collection](https://laravel.com/docs/collections) object, use `all()` for array)* 184 | 185 | ``` 186 | [0] => Array 187 | ( 188 | [date] => 2019-01-23 189 | [campaign_id] => 00000000 190 | [campaign_name] => Campaign Name 191 | [impressions] => 3 192 | [clicks] => 0 193 | [cost] => 0 194 | [conversions] => 0.00 195 | [conversion_value] => 0.00 196 | [final_url] => https://your-final-url.com/landing-page 197 | ) 198 | ... 199 | ``` 200 | 201 | ### Placement Domain Performance Report 202 | 203 | This report is aggregated to **account-level** and uses [Placement Performance](https://developers.google.com/adwords/api/docs/appendix/reports/placement-performance-report). 204 | 205 | ```php 206 | $report = $googleAds->reports($dateFrom, $dateTo) 207 | ->getPlacementReport(); 208 | ``` 209 | 210 | *Results: `getPlacementReport()` (returns a [Laravel Collection](https://laravel.com/docs/collections) object, use `all()` for array)* 211 | 212 | ``` 213 | // the key of the array is also the placement domain 214 | // this ensures a unique placement aggregation 215 | 216 | [example.com] => Array 217 | ( 218 | [placement] => example.com 219 | [impressions] => 9 220 | [clicks] => 0 221 | [cost] => 0 222 | [conversions] => 0.00 223 | [conversion_value] => 0.00 224 | ) 225 | ... 226 | ``` 227 | 228 | ### Placement URL Performance Report 229 | 230 | This report is aggregated to **account-level** and uses [URL Performance](https://developers.google.com/adwords/api/docs/appendix/reports/url-performance-report). 231 | 232 | ```php 233 | $report = $googleAds->reports($dateFrom, $dateTo) 234 | ->getPlacementUrlReport(); 235 | ``` 236 | 237 | *Results: `getPlacementUrlReport()` (returns a [Laravel Collection](https://laravel.com/docs/collections) object, use `all()` for array)* 238 | 239 | ``` 240 | // the key of the array is also the placement URL 241 | // this ensures a unique placement URL aggregation 242 | 243 | [example.com/page/path.html] => Array 244 | ( 245 | [placement] => example.com/page/path.html 246 | [impressions] => 9 247 | [clicks] => 0 248 | [cost] => 0 249 | [conversions] => 0.00 250 | [conversion_value] => 0.00 251 | ) 252 | ... 253 | ``` 254 | 255 | 256 | ### Search Term Performance Report 257 | 258 | This report is aggregated to **account-level** and uses [Search Query Performance](https://developers.google.com/adwords/api/docs/appendix/reports/search-query-performance-report). 259 | 260 | ```php 261 | $report = $googleAds->reports($dateFrom, $dateTo) 262 | ->getSearchTermReport(); 263 | ``` 264 | 265 | *Results: `getSearchTermReport()` (returns a [Laravel Collection](https://laravel.com/docs/collections) object, use `all()` for array)* 266 | 267 | ``` 268 | // the key of the array is also the search term 269 | // this ensures a unique search term for aggregation 270 | 271 | [food in boston ma] => Array 272 | ( 273 | [search_term] => food in boston ma 274 | [impressions] => 1 275 | [clicks] => 1 276 | [cost] => 0.47 277 | [conversions] => 0.00 278 | [conversion_value] => 0.00 279 | ) 280 | ... 281 | ``` 282 | 283 | ### Age Range Performance Report 284 | 285 | This report is aggregated to **account-level** and uses [Age Range Performance](https://developers.google.com/adwords/api/docs/appendix/reports/age-range-performance-report). 286 | 287 | ```php 288 | $report = $googleAds->reports($dateFrom, $dateTo) 289 | ->getAgeRangeReport(); 290 | ``` 291 | 292 | *Results: `getGenderReport()` (returns a [Laravel Collection](https://laravel.com/docs/collections) object, use `all()` for array)* 293 | 294 | ``` 295 | // the key of the array is also the age range 296 | // this ensures a unique age range for aggregation 297 | // Age (Valid age ranges are 18-24, 25-34, 35-44, 45-54, 55-64, 65 or more, and Unknown.) 298 | 299 | [18-24] => Array 300 | ( 301 | [age_range] => 18-24 302 | [impressions] => 12517 303 | [clicks] => 203 304 | [cost] => 58.65 305 | [conversions] => 11 306 | [conversion_value] => 51.25 307 | ) 308 | ... 309 | ``` 310 | 311 | ### Gender Performance Report 312 | 313 | This report is aggregated to **account-level** and uses [Gender Performance](https://developers.google.com/adwords/api/docs/appendix/reports/gender-performance-report). 314 | 315 | ```php 316 | $report = $googleAds->reports($dateFrom, $dateTo) 317 | ->getGenderReport(); 318 | ``` 319 | 320 | *Results: `getGenderReport()` (returns a [Laravel Collection](https://laravel.com/docs/collections) object, use `all()` for array)* 321 | 322 | ``` 323 | // the key of the array is also the gender 324 | // this ensures a unique gender for aggregation 325 | // Genders (Male, Female) 326 | 327 | [Female] => Array 328 | ( 329 | [gender] => Female 330 | [impressions] => 16045 331 | [clicks] => 411 332 | [cost] => 162.28 333 | [conversions] => 18 334 | [conversion_value] => 88 335 | ) 336 | ... 337 | ``` 338 | 339 | 340 | ## Campaigns 341 | 342 | You can easily manage/view campaign settings by loading up the campaign service. 343 | 344 | ```php 345 | $campaign = $googleAds->campaign('CAMPAIGN_ID'); 346 | ``` 347 | 348 | **Available methods** from the `Campaign` object. (the `SET` methods are chainable, see example) 349 | 350 | |Method |Description | 351 | |--- |--- | 352 | |`getId()`|GET the Campaign id| 353 | |`getName()`|GET the Campaign name| 354 | |`getStatus()`|GET the Campaign Status (`ENABLED`,`PAUSED`,`DELETED`)| 355 | |`getBudget()`|GET the Campaign budget| 356 | |`getBudgetDelivery()`|GET the Campaign budget delivery (`ACCELERATED`,`STANDARD`)| 357 | |`getBidStrategy()`|GET the Campaign bid strategy (`CPA`,`CPC`,`ECPC`)| 358 | |`getTargetCpa()`|GET the Campaign Target CPA (if one exists)| 359 | |`setId()`|SET the Campaign id| 360 | |`setName()`|SET the Campaign name| 361 | |`setStatus($status)`|SET Campaign status (`ENABLED`,`PAUSED`)| 362 | |`setBudget($amount)`|SET Campaign Budget (`FLOAT $amount`)| 363 | |`setTargetCpa($amount)`|SET Campaign Target CPA (`FLOAT $amount`)| 364 | |`save()`|Post your changes to the server| 365 | 366 | 367 | ### Example Usage 368 | 369 | ```php 370 | // Rename the campaign, enable it, and then set the budget to $300 371 | $googleAds->campaign('CAMPAIGN_ID') 372 | ->setName('My New Campaign Name') 373 | ->setStatus('ENABLED') 374 | ->setBudget(300) 375 | ->save(); 376 | 377 | // if you only want to grab the campaign name... 378 | $campaignName = $googleAds->campaign('CAMPAIGN_ID')->getName(); 379 | 380 | // Get only the campaign status 381 | $campaignStatus = $googleAds->campaign('CAMPAIGN_ID')->getStatus(); 382 | 383 | ``` 384 | 385 | 386 | ## Ad Groups 387 | 388 | You can easily manage/view Ad Group settings by loading up the AdGroup service. 389 | 390 | ```php 391 | $adGroup = $googleAds->adGroup('ADGROUP_ID'); 392 | ``` 393 | 394 | **Available methods** from the `AdGroup` object. (the `SET` methods are chainable, see example) 395 | 396 | |Method |Description | 397 | |--- |--- | 398 | |`getId()`|GET the AdGroup id| 399 | |`getName()`|GET the AdGroup name| 400 | |`getCampaignId()`|GET the AdGroup Campaign Id| 401 | |`getStatus()`|GET the AdGroup Status (`ENABLED`,`PAUSED`,`DELETED`)| 402 | |`getBidStrategy()`|GET the AdGroup bid strategy (`CPA`,`CPC`,`ECPC`)| 403 | |`getBid()`|GET the AdGroup Bid Amount| 404 | |`getType()`|GET the AdGroup Type| 405 | |`setId()`|SET the AdGroup id| 406 | |`setName()`|SET the AdGroup name| 407 | |`setStatus($status)`|SET AdGroup status (`ENABLED`,`PAUSED`)| 408 | |`setBid($amount)`|SET AdGroup Bid (`FLOAT $amount`) *currently only the CPC/ECPC bid*| 409 | |`save()`|Post your changes to the server| 410 | 411 | ### Example Usage 412 | 413 | ```php 414 | // Rename the Ad Group, enable it, and then set the CPC bid to 0.22 415 | $googleAds->adGroup('ADGROUP_ID') 416 | ->setName('My New Ad Group Name') 417 | ->setStatus('ENABLED') 418 | ->setBid(0.22) 419 | ->save(); 420 | 421 | // if you only want to grab the ad group name... 422 | $adgroupName = $googleAds->adGroup('ADGROUP_ID')->getName(); 423 | 424 | // Get only the ad group status 425 | $adgroupStatus = $googleAds->adGroup('ADGROUP_ID')->getStatus(); 426 | 427 | // Get only the ad group bid 428 | $adGroupBid = $googleAds->adGroup('ADGROUP_ID')->getBid(); 429 | 430 | ``` 431 | 432 | ## Offline Conversion Import 433 | 434 | You can import offline conversions using this simple method. Uses [OfflineConversionFeedService](https://developers.google.com/adwords/api/docs/reference/v201809/OfflineConversionFeedService) 435 | 436 | ```php 437 | // Can chain and add() as many as you wish 438 | $conversionImport = $googleAds->offlineConversionImport() 439 | ->add([ 440 | 'click_id' => 'CjwKCAjwzJjrBRBvEiwA867byorDPQ2K0zO_8bXuJ7SeEs', 441 | 'value' => 0, 442 | 'name' => 'CONVERSION NAME', 443 | 'time' => 'DATETIME TIMEZONE' 444 | ]) 445 | ->add([ 446 | 'click_id' => 'CjwKCAjwzJjrBRBvEiwA867', 447 | 'value' => 0, 448 | 'name' => 'CONVERSION NAME', 449 | 'time' => 'DATETIME TIMEZONE' 450 | ]); 451 | 452 | // when read, begin the upload 453 | // Response will return an array of [success] and [errors] 454 | $response = $conversionImport->upload(); 455 | 456 | // passing true will return a more detail array of [success] and [errors] for each upload 457 | $response = $conversionImport->upload(true); 458 | 459 | ``` 460 | 461 | *Note: `time` must include the timezone. "20190828 200112 America/New_York" (format "Ymd His timezone")* 462 | 463 | **Methods:** 464 | 465 | |Method|Description| 466 | |---|---| 467 | |`add( single array )`|Adding a single conversion 468 | |`addBulk( multi-array )`|Adding an array of single conversions 469 | |`upload()`|Imports the conversions to Google (pass `true` or `false` as arg to return more detail success array) 470 | 471 | **Response:** 472 | 473 | The array response is both `success` and `errors`, errors will include [Google Error Codes](https://developers.google.com/adwords/api/docs/reference/v201809/OfflineConversionFeedService.OfflineConversionError.Reason) 474 | 475 | *Note: Click Ids that are success will not appear in the errors array and vise-versa.* 476 | 477 | ``` 478 | Array 479 | ( 480 | [errors] => Array 481 | ( 482 | [0] => Array 483 | ( 484 | [click_id] => CjwKCAjwzJjrBRBvEiwA867 485 | [error] => TOO_RECENT_CLICK 486 | ) 487 | ) 488 | [success] => Array 489 | ( 490 | [1] => CjwKCAjwzJjrBRBvEiwA867byorDPQ2K0zO_8bXuJ7SeEs 491 | ) 492 | ) 493 | ... 494 | ``` 495 | 496 | 497 | ## Manual Configuration 498 | 499 | By default, the configuration will always look at the `/config/google-ads.php`, however, you can override that by injecting your own config into the google ads service object. 500 | 501 | **You only need to use this if you WANT to override the config, otherwise the config file will work in most cases.** 502 | 503 | ```php 504 | $googleAds = LaravelAds::googleAds(); 505 | $googleAds->configuration([ 506 | 'ADWORDS' => [ 507 | 'developerToken' => '' 508 | ], 509 | 'ADWORDS_REPORTING' => [ 510 | 511 | ], 512 | 'OAUTH2' => [ 513 | 'clientId' => '', 514 | 'clientSecret' => '', 515 | 'refreshToken' => '', 516 | ], 517 | 'LOGGING' => [ 518 | 'soapLogLevel' => 'ERROR', 519 | 'reportDownloaderLogLevel' => 'ERROR' 520 | ] 521 | ]); 522 | 523 | $googleAds = $googleAds->with('ACCOUNT_ID'); 524 | 525 | // after the config is set above, now you can use the SDK as you normally do... 526 | // $report = $googleAds->reports('2020-01-01', '2020-01-05')->getAccountReport(); 527 | 528 | ``` 529 | 530 | 531 | ## Need More? Advanced Options 532 | 533 | If the pre-built methods don't have what you're looking for, you may need to write your own methods to communicate with the AdWords Services directly. **You will need to understand how the [Google Ads API](https://developers.google.com/adwords/api/docs/reference/release-notes/v201809) and [Google Ads SDK](https://github.com/googleads/googleads-php-lib) work.** 534 | 535 | ```php 536 | // You'll need to include the correct namespaces included in the Google Ads SDK 537 | use Google\AdsApi\AdWords\v201809\cm\CampaignService; 538 | 539 | // Start the initial step authenticating to a service 540 | $googleAds = LaravelAds::googleAds()->with('CLIENT_ID'); 541 | 542 | // this communicates with the GoogleAds PHP LIB (returns AdWordsServices) 543 | // return the service class so that you can manage the next step 544 | // replace the "CampaignService::class" with the API service you want to use 545 | $campaignService = $googleAds->call(CampaignService::class); 546 | 547 | // ... write your logic here to understand the API response 548 | 549 | ``` 550 | 551 | ## Jump To: 552 | * [Home](README.md) 553 | * [GoogleAds - Getting Started](GoogleAds-SDK.md) 554 | * [BingAds - Getting Started](BingAds-SDK.md) 555 | * [FacebookAds - Getting Started](FacebookAds-SDK.md) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Timothy Marois < timothymarois@gmail.com > 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laravel Ads SDK 2 | 3 | 4 | 5 | [Join the Discord](https://discord.gg/g3W49zdWm2) – For support, updates and collaboration. 6 | 7 | For **Google Ads**, **Bing Ads** and **Facebook Ads** API. 8 | 9 | This is a wrapper for connecting each ad source into your Laravel application. This SDK provides a cleaner and a more consistent integration across many ad platforms than the official SDKs, and ultimately making it a lot easier to implement in your projects. You shouldn't have to learn how to communicate and understand the responses to every API. 10 | 11 | # Installation 12 | 13 | Use [Composer](http://getcomposer.org/) to install package. 14 | 15 | Run `composer require tmarois/laravel-ads-sdk` 16 | 17 | # Config 18 | 19 | 1) Run `php artisan vendor:publish --tag=laravel-ads-sdk` 20 | 21 | 2) Copy this to your `.env` and update with your credentials (if you dont have credentials, continue to the next step). 22 | 23 | ``` 24 | ADWORDS_DEVELOPER_TOKEN="" 25 | ADWORDS_OAUTH2_CLIENT_ID="" 26 | ADWORDS_OAUTH2_CLIENT_SECRET="" 27 | ADWORDS_OAUTH2_REFRESH_TOKEN="" 28 | 29 | BING_DEVELOPER_TOKEN="" 30 | BING_CLIENT_ID="" 31 | BING_CLIENT_SECRET="" 32 | BING_REFRESH_TOKEN="" 33 | 34 | FB_APP_ID="" 35 | FB_APP_SECRET="" 36 | FB_ACCESS_TOKEN="" 37 | FB_REFRESH_TOKEN="" 38 | ``` 39 | 40 | # Authentication 41 | 42 | ### :point_right: For GoogleAds 43 | 44 | You will need your developer token, client id and client secret to continue. [Learn More](GoogleAds-Auth.md) 45 | 46 | *Follow the steps in the command line to **generate a refresh token**.* 47 | 48 | Run `php artisan laravelads:token:generate --service=GoogleAds` 49 | 50 | Having Trouble? [Learn More](GoogleAds-Auth.md) 51 | 52 | ### :point_right: For BingAds 53 | 54 | You will need your developer token, client id and client secret to continue. [Learn More](BingAds-Auth.md) 55 | 56 | *Follow the steps in the command line to **generate a refresh token**.* 57 | 58 | Run `php artisan laravelads:token:generate --service=BingAds` 59 | 60 | Having Trouble? [Learn More](BingAds-Auth.md) 61 | 62 | ### :point_right: For FacebookAds 63 | 64 | You will need your app id, app secret and access token. [Learn More](FacebookAds-Auth.md) 65 | 66 | *NOTE: It appears for facebook, you do not need to generate refresh token, once you have your access token, the api should connect as long as you've given yourself the correct access, permissions, scopes to marketing api and ads account.* 67 | 68 | # :rocket: Usage 69 | 70 | Accessing `GoogleAds`, `BingAds` or `FacebookAds` use the following: 71 | 72 | ```php 73 | // The namespace to the Facade for laravel Ads SDK 74 | use LaravelAds; 75 | 76 | // calling Google Ads and including the Account ID 77 | $googleAds = LaravelAds::googleAds()->with('ACCOUNT_ID'); 78 | 79 | // calling Bing Ads and including the Account ID 80 | $bingAds = LaravelAds::bingAds()->with('ACCOUNT_ID'); 81 | 82 | // calling Facebook Ads and including the Account ID 83 | $facebookAds = LaravelAds::facebookAds()->with('ACCOUNT_ID'); 84 | ``` 85 | 86 | # Google Ads 87 | 88 | This uses the [googleads-php-lib](https://github.com/googleads/googleads-php-lib) SDK for the [Google Ads API](https://developers.google.com/adwords/api/docs/guides/start) 89 | 90 | > **NOTICE** – You will need to [Request Google Ads API Access](https://services.google.com/fb/forms/newtoken/). 91 | 92 | [Need help with authentication?](GoogleAds-Auth.md) 93 | 94 | #### Management 95 | * [Fetching - All Campaigns](GoogleAds-SDK.md#fetch-all-campaigns) 96 | * [Fetching - All Ad Groups](GoogleAds-SDK.md#fetch-all-ad-groups) 97 | * [Management - Campaigns](GoogleAds-SDK.md#campaigns) 98 | * [Management - Ad Groups](GoogleAds-SDK.md#ad-groups) 99 | * [Offline Conversion Import](GoogleAds-SDK.md#offline-conversion-import) 100 | * [Manual Configuration](GoogleAds-SDK.md#manual-configuration) 101 | * [Advanced Options](GoogleAds-SDK.md#need-more-advanced-options) 102 | 103 | #### Reports 104 | * [Account Performance](GoogleAds-SDK.md#account-reports) 105 | * [Campaign Performance](GoogleAds-SDK.md#campaign-reports) 106 | * [Ad Group Performance](GoogleAds-SDK.md#ad-group-reports) 107 | * [Final URL Performance](GoogleAds-SDK.md#final-url-performance-report) 108 | * [Placement Domain Performance](GoogleAds-SDK.md#placement-domain-performance-report) 109 | * [Placement URL Performance](GoogleAds-SDK.md#placement-url-performance-report) 110 | * [Search Term Performance](GoogleAds-SDK.md#search-term-performance-report) 111 | * [Age Range Performance](GoogleAds-SDK.md#age-range-performance-report) 112 | * [Gender Performance](GoogleAds-SDK.md#gender-performance-report) 113 | 114 | # Bing Ads 115 | 116 | This uses the [BingAds-PHP-SDK](https://github.com/BingAds/BingAds-PHP-SDK) for the [Bing Ads API](https://docs.microsoft.com/en-us/bingads/guides/get-started-php?view=bingads-12) 117 | 118 | > **NOTICE** – You will need to [Request Bing Ads API Access](https://advertise.bingads.microsoft.com/en-us/resources/bing-partner-program/request-bing-ads-api-access). 119 | 120 | [Need help with authentication or sandbox mode?](BingAds-Auth.md) 121 | 122 | #### Management 123 | * [Fetching - Get Customers](BingAds-SDK.md#fetch-customers) 124 | * [Fetching - All Campaigns](BingAds-SDK.md#fetch-all-campaigns) 125 | * [Fetching - All Ad Groups](BingAds-SDK.md#fetch-all-ad-groups) 126 | * [Management - Campaigns](BingAds-SDK.md#campaigns) 127 | * [Management - Ad Groups](BingAds-SDK.md#ad-groups) 128 | * [Offline Conversion Import](BingAds-SDK.md#offline-conversion-import) 129 | * [Manual Configuration](BingAds-SDK.md#manual-configuration) 130 | 131 | #### Reports 132 | * [Account Performance](BingAds-SDK.md#account-reports) 133 | * [Campaign Performance](BingAds-SDK.md#campaign-reports) 134 | * [Ad Group Performance](BingAds-SDK.md#ad-group-reports) 135 | * [Final URL Performance](BingAds-SDK.md#final-url-performance-report) 136 | * [Search Term Performance](BingAds-SDK.md#search-term-performance-report) 137 | * [Age Range Performance](BingAds-SDK.md#age-range-performance-report) 138 | * [Gender Performance](BingAds-SDK.md#gender-performance-report) 139 | * [Custom Fields](BingAds-SDK.md#custom-fields) 140 | 141 | 142 | # Facebook Ads 143 | 144 | This uses the [facebook-php-business-sdk](https://github.com/facebook/facebook-php-business-sdk) for [Facebook Marketing API](https://developers.facebook.com/docs/marketing-apis) 145 | 146 | #### Management 147 | * [Fetching - Campaigns](FacebookAds-SDK.md#white_check_mark-fetch-campaigns) 148 | * [Fetching - Ad Groups](FacebookAds-SDK.md#white_check_mark-fetch-ad-groups) 149 | 150 | #### Reports 151 | * [Account Performance](FacebookAds-SDK.md#white_check_mark-account-reports) 152 | * [Campaign Performance](FacebookAds-SDK.md#white_check_mark-campaign-reports) 153 | * [Ad Group Performance](FacebookAds-SDK.md#white_check_mark-ad-group-reports) 154 | 155 | # Customization 156 | 157 | We realize that we can't add every endpoint so in order to help improve your developer experience, we have made the Service classes Macroable. Macros are a way to add a new custom method to the classes. This way you are able to utilize the existing auth and all of the other goodies that come with this package. 158 | 159 | Typically, you should call this method from the boot method of one of your application's service providers, such as the `App\Providers\AppServiceProvider` service provider: 160 | 161 | ```php 162 | public function boot() 163 | { 164 | LaravelAds\Services\BingAds\Service::macro('addUetTags', function($tags){ 165 | $serviceCall = $this->call(ServiceClientType::CampaignManagementVersion13); 166 | 167 | try { 168 | $request = new AddUetTagsRequest(); 169 | $request->UetTags = $tags; 170 | 171 | $serverResponse = $serviceCall->GetService()->AddUetTags($request); 172 | 173 | return $serverResponse; 174 | } catch (\Exception $e) { 175 | print $serviceCall->GetService()->__getLastRequest()."\n"; 176 | print $serviceCall->GetService()->__getLastResponse()."\n"; 177 | } 178 | }); 179 | 180 | LaravelAds\Services\GoogleAds\Service::macro('dd', function(){ 181 | dd($this); 182 | }); 183 | } 184 | ``` 185 | 186 | Then in your controller or job you would call: 187 | 188 | ```php 189 | $bingAds = LaravelAds::bingAds()->addUetTags([ 190 | [ 191 | 'Name' => 'Extensible!', 192 | 'Description' => 'No PR Needed!', 193 | ] 194 | ]); 195 | 196 | $bingAds = LaravelAds::googleAds()->dd(); 197 | ``` 198 | 199 | # Contributions 200 | 201 | We are actively looking for new contributors. 202 | 203 | If you want to contribute, [Join the Discord](https://discord.gg/g3W49zdWm2) channel and/or submit pull requests. 204 | 205 | # License 206 | 207 | **Laravel Ads SDK** (This Package) is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT). USE AT YOUR OWN RISK. Laravel Ads SDK is a tool to help you manage your accounts, it does not guarantee features listed here will work as described. If you do find a bug, please feel free to submit an issue. *This package is not affiliated with Laravel LLC or the Laravel Framework team.* 208 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "A Laravel Ads SDK for Google Ads, Bing Ads and Facebook Ads API", 3 | "keywords": ["ads", "google-ads","bing-ads", "api", "facebook-ads"], 4 | "name": "tmarois/laravel-ads-sdk", 5 | "homepage": "https://github.com/tmarois/laravel-ads-sdk", 6 | "type": "package", 7 | "minimum-stability": "stable", 8 | "authors": [ 9 | { 10 | "name": "Timothy Marois", 11 | "email": "timothymarois@gmail.com" 12 | } 13 | ], 14 | "require": { 15 | "php": ">=7", 16 | "googleads/googleads-php-lib": "^60.0", 17 | "microsoft/bingads": "13.0.15.2", 18 | "facebook/php-business-sdk": "^17.0" 19 | }, 20 | 21 | "require-dev": { 22 | "php-coveralls/php-coveralls": "^2.0", 23 | "phpunit/phpunit": "6.*" 24 | }, 25 | 26 | "autoload": { 27 | "psr-4": { 28 | "LaravelAds\\": "src/" 29 | } 30 | }, 31 | "extra": { 32 | "laravel": { 33 | "providers": [ 34 | "LaravelAds\\Providers\\LaravelAdsProvider" 35 | ], 36 | "aliases": { 37 | "LaravelAds": "LaravelAds\\Facades\\LaravelAds" 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /config/bing-ads.php: -------------------------------------------------------------------------------- 1 | env('BING_DEVELOPER_TOKEN', ''), 6 | 7 | 'clientId' => env('BING_CLIENT_ID', ''), 8 | 9 | 'clientSecret' => env('BING_CLIENT_SECRET', ''), 10 | 11 | 'refreshToken' => env('BING_REFRESH_TOKEN', ''), 12 | 13 | 'redirect_uri' => env('BING_REDIRECT_URI', 'https://login.microsoftonline.com/common/oauth2/nativeclient'), 14 | ]; 15 | -------------------------------------------------------------------------------- /config/facebook-ads.php: -------------------------------------------------------------------------------- 1 | env('FB_APP_ID', ''), 6 | 7 | 'app_secret' => env('FB_APP_SECRET', ''), 8 | 9 | 'access_token' => env('FB_ACCESS_TOKEN', ''), 10 | 11 | 'refresh_token' => env('FB_REFRESH_TOKEN', '') 12 | ]; 13 | -------------------------------------------------------------------------------- /config/google-ads.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'developerToken' => env('ADWORDS_DEVELOPER_TOKEN', ''), 6 | 7 | /* 8 | * Optional. Set a friendly application name identifier. 9 | * 10 | * 'userAgent' => '', 11 | */ 12 | 13 | /* 14 | * Optional additional AdWords API settings. 15 | * endpoint = "https://adwords.google.com/" 16 | * 17 | * 'isPartialFailure' => false, 18 | */ 19 | 20 | /* 21 | * Optional setting for utility usage tracking in the user agent in requests. 22 | * Defaults to true. 23 | * 24 | * 'includeUtilitiesInUserAgent' => true, 25 | */ 26 | ], 27 | 28 | 'DFP' => [ 29 | 'networkCode' => '', 30 | 'applicationName' => '', 31 | ], 32 | 33 | 'ADWORDS_REPORTING' => [ 34 | /* 35 | * Optional reporting settings. 36 | * 37 | * 'isSkipReportHeader' => false, 38 | * 'isSkipColumnHeader' => false, 39 | * 'isSkipReportSummary' => false, 40 | * 'isUseRawEnumValues' => false, 41 | */ 42 | ], 43 | 44 | 'OAUTH2' => [ 45 | /* 46 | * Required OAuth2 credentials. Uncomment and fill in the values for the 47 | * appropriate flow based on your use case. See the README for guidance: 48 | * https://github.com/googleads/googleads-php-lib/blob/master/README.md#getting-started 49 | */ 50 | 51 | 'clientId' => env('ADWORDS_OAUTH2_CLIENT_ID', ''), 52 | 'clientSecret' => env('ADWORDS_OAUTH2_CLIENT_SECRET', ''), 53 | 'refreshToken' => env('ADWORDS_OAUTH2_REFRESH_TOKEN', ''), 54 | 55 | /* 56 | * For service account flow. 57 | * 'jsonKeyFilePath' => 'INSERT_ABSOLUTE_PATH_TO_OAUTH2_JSON_KEY_FILE_HERE' 58 | * 'scopes' => 'https://www.googleapis.com/auth/adwords', 59 | */ 60 | ], 61 | 62 | 'SOAP' => [ 63 | /* 64 | * Optional SOAP settings. See SoapSettingsBuilder.php for more information. 65 | * 'compressionLevel' => , 66 | * 'wsdlCache' => , 67 | */ 68 | ], 69 | 70 | 'PROXY' => [ 71 | /* 72 | * Optional proxy settings to be used by SOAP requests. 73 | * 'host' => '', 74 | * 'port' => , 75 | * 'user' => '', 76 | * 'password' => '', 77 | */ 78 | ], 79 | 80 | 'LOGGING' => [ 81 | /* 82 | * Optional logging settings. 83 | * 'soapLogFilePath' => 'path/to/your/soap.log', 84 | * 'soapLogLevel' => 'INFO', 85 | * 'reportDownloaderLogFilePath' => 'path/to/your/report-downloader.log', 86 | * 'reportDownloaderLogLevel' => 'INFO', 87 | * 'batchJobsUtilLogFilePath' => 'path/to/your/bjutil.log', 88 | * 'batchJobsUtilLogLevel' => 'INFO', 89 | */ 90 | ], 91 | ]; 92 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "version": "1.5.0", 4 | "scripts": { 5 | "release": "dotenv release-it" 6 | }, 7 | "devDependencies": { 8 | "@release-it/keep-a-changelog": "^2.2.2", 9 | "auto-changelog": "^2.2.1", 10 | "dotenv-cli": "^4.0.0", 11 | "release-it": "^14.2.2" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Console/RefreshTokenCommand.php: -------------------------------------------------------------------------------- 1 | option('service')); 37 | 38 | switch ($service) { 39 | case 'googleads': 40 | $this->googleAdsRefresh(); 41 | break; 42 | case 'bingads': 43 | $this->bingAdsRefresh(); 44 | break; 45 | default: 46 | $this->error("Error: --service option is required. (Use GoogleAds or BingAds)"); 47 | } 48 | } 49 | 50 | /** 51 | * googleAdsRefresh 52 | * 53 | */ 54 | protected function googleAdsRefresh() 55 | { 56 | $config = config('google-ads')['OAUTH2'] ?? []; 57 | 58 | // check if the config is right 59 | if (!$config) { 60 | return $this->error('Your Google Ads config is not setup properly. Aborting.'); 61 | } 62 | 63 | $clientId = $config['clientId']; 64 | $clientSecret = $config['clientSecret']; 65 | 66 | $scopes = 'https://www.googleapis.com/auth/adwords'; 67 | $authorizationUri = 'https://accounts.google.com/o/oauth2/v2/auth'; 68 | $redirectUri = 'urn:ietf:wg:oauth:2.0:oob'; 69 | 70 | $oauth2 = new OAuth2([ 71 | 'authorizationUri' => $authorizationUri, 72 | 'redirectUri' => $redirectUri, 73 | 'tokenCredentialUri' => CredentialsLoader::TOKEN_CREDENTIAL_URI, 74 | 'clientId' => $clientId, 75 | 'clientSecret' => $clientSecret, 76 | 'scope' => $scopes 77 | ]); 78 | 79 | // print first message 80 | $this->line(sprintf( 81 | "Please sign in to your Google Ads account, and open following url:\n%s", 82 | $oauth2->buildFullAuthorizationUri([ 83 | 'access_type' => 'offline' 84 | ]) 85 | )); 86 | 87 | // Retrieve token 88 | $accessToken = $this->ask('Insert your access token'); 89 | 90 | // Fetch auth token 91 | try { 92 | $oauth2->setCode($accessToken); 93 | $authToken = $oauth2->fetchAuthToken(); 94 | } catch (Exception $exception) { 95 | return $this->error($exception->getMessage()); 96 | } 97 | 98 | if (!isset($authToken)) { 99 | return $this->error('Error fetching the refresh token'); 100 | } 101 | 102 | $this->comment('Copy the refresh token and paste the value on ADWORDS_OAUTH2_REFRESH_TOKEN in your .env'); 103 | 104 | // Print refresh token 105 | $this->line(sprintf( 106 | 'Refresh token: "%s"', 107 | $authToken['refresh_token'] 108 | )); 109 | } 110 | 111 | /** 112 | * googleAdsRefresh 113 | * 114 | */ 115 | protected function bingAdsRefresh() 116 | { 117 | $config = config('bing-ads') ?? []; 118 | 119 | // check if the config is right 120 | if (!$config) { 121 | return $this->error('Your Bing Ads config is not setup properly. Aborting.'); 122 | } 123 | 124 | $clientId = $config['clientId']; 125 | $clientSecret = $config['clientSecret']; 126 | $developerToken = $config['developerToken']; 127 | $redirectUri = $config['redirect_uri'] ?? 'https://login.microsoftonline.com/common/oauth2/nativeclient'; 128 | 129 | $authentication = (new OAuthWebAuthCodeGrant()) 130 | ->withClientId($clientId) 131 | ->withClientSecret($clientSecret) 132 | ->withRedirectUri($redirectUri) 133 | ->withState(rand(0, 999999999)); 134 | 135 | $AuthorizationData = (new AuthorizationData()) 136 | ->withAuthentication($authentication) 137 | ->withDeveloperToken($developerToken); 138 | 139 | $this->comment("Please sign in to your Bing Ads account, and open following url:"); 140 | $this->line(str_replace(' ','%20',$AuthorizationData->Authentication->GetAuthorizationEndpoint())); 141 | 142 | $accessToken = $this->ask('Insert the FULL URL that you were redirected to (after you approve the access):'); 143 | 144 | try { 145 | $AuthorizationData->Authentication->RequestOAuthTokensByResponseUri($accessToken); 146 | } catch (Exception $exception) { 147 | return $this->error($exception->getMessage()); 148 | } 149 | 150 | $this->comment('Copy the refresh token and paste the value on BING_REFRESH_TOKEN in your .env'); 151 | 152 | // Print refresh token 153 | $this->line(sprintf( 154 | 'Refresh token: "%s"', 155 | $AuthorizationData->Authentication->OAuthTokens->RefreshToken 156 | )); 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/Facades/LaravelAds.php: -------------------------------------------------------------------------------- 1 | publishes([ 18 | __DIR__.'/../../config/google-ads.php' => (function_exists('config_path') ? config_path('google-ads.php') : 'google-ads.php') 19 | ], 'laravel-ads-sdk'); 20 | 21 | $this->publishes([ 22 | __DIR__.'/../../config/bing-ads.php' => (function_exists('config_path') ? config_path('bing-ads.php') : 'bing-ads.php') 23 | ], 'laravel-ads-sdk'); 24 | 25 | $this->publishes([ 26 | __DIR__.'/../../config/facebook-ads.php' => (function_exists('config_path') ? config_path('facebook-ads.php') : 'facebook-ads.php') 27 | ], 'laravel-ads-sdk'); 28 | } 29 | 30 | /** 31 | * Register the application services. 32 | * 33 | * @return void 34 | */ 35 | public function register() 36 | { 37 | $this->commands([ 38 | RefreshTokenCommand::class, 39 | ]); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Services/BingAds/Fetch.php: -------------------------------------------------------------------------------- 1 | service = $service; 32 | } 33 | 34 | /** 35 | * getCampaigns() 36 | * 37 | * 38 | * @return object Collection 39 | */ 40 | public function getCampaigns($returnArray = true) 41 | { 42 | $serviceCall = $this->service->call(ServiceClientType::CampaignManagementVersion13); 43 | 44 | $request = new GetCampaignsByAccountIdRequest(); 45 | $request->AccountId = $this->service->getClientId(); 46 | 47 | $r = []; 48 | 49 | try { 50 | $items = $serviceCall->GetService()->GetCampaignsByAccountId($request); 51 | } catch (\Exception $e) { 52 | return []; 53 | } 54 | 55 | if ($items && isset($items->Campaigns, $items->Campaigns->Campaign)) { 56 | foreach ($items->Campaigns->Campaign as $item) { 57 | $campaign = $this->service->campaign($item); 58 | 59 | if ($returnArray) { 60 | $r[] = [ 61 | 'id' => $campaign->getId(), 62 | 'name' => $campaign->getName(), 63 | 'status' => $campaign->getStatus(), 64 | 'channel' => $campaign->getChannelType(), 65 | 'budget' => $campaign->getBudget(), 66 | 'bid_strategy' => $campaign->getBidStrategy(), 67 | 'target_cpa' => $campaign->getTargetCpa() 68 | ]; 69 | } else { 70 | $r[] = $campaign; 71 | } 72 | } 73 | } 74 | 75 | return collect($r); 76 | } 77 | 78 | /** 79 | * getAdGroups() 80 | * 81 | * 82 | * @return object Collection 83 | */ 84 | public function getAdGroups($returnArray = true) 85 | { 86 | $serviceCall = $this->service->call(ServiceClientType::CampaignManagementVersion13); 87 | 88 | $campaigns = $this->getCampaigns(); 89 | 90 | $r = []; 91 | foreach ($campaigns->all() as $campaign) { 92 | $request = new GetAdGroupsByCampaignIdRequest(); 93 | $request->CampaignId = $campaign['id']; 94 | 95 | try { 96 | $items = $serviceCall->GetService()->GetAdGroupsByCampaignId($request); 97 | 98 | foreach ($items->AdGroups->AdGroup as $item) { 99 | $adgroup = $this->service->adGroup($item); 100 | 101 | if ($returnArray) { 102 | $r[] = [ 103 | 'id' => $adgroup->getId(), 104 | 'name' => $adgroup->getName(), 105 | 'status' => $adgroup->getStatus(), 106 | 'campaign_id' => $request->CampaignId, 107 | 'type' => 'SEARCH', 108 | 'bid_strategy' => $adgroup->getBidStrategy(), 109 | 'bid' => $adgroup->getBid() 110 | ]; 111 | } else { 112 | $r[] = $adgroup; 113 | } 114 | } 115 | } catch (\Exception $e) { 116 | continue; 117 | } 118 | } 119 | 120 | return collect($r); 121 | } 122 | 123 | public function getCustomers($returnArray = true) 124 | { 125 | $serviceCall = $this->service->call(ServiceClientType::CustomerManagementVersion13); 126 | 127 | $request = new GetCustomersInfoRequest(); 128 | $request->CustomerNameFilter = ''; 129 | $request->TopN = 100; 130 | 131 | try { 132 | $items = $serviceCall->GetService()->GetCustomersInfo($request); 133 | 134 | foreach ($items->CustomersInfo->CustomerInfo as $item) { 135 | $customer = $this->service->customer($item); 136 | 137 | if ($returnArray) { 138 | $r[] = [ 139 | 'id' => $customer->getId(), 140 | 'name' => $customer->getName(), 141 | ]; 142 | } else { 143 | $r[] = $customer; 144 | } 145 | } 146 | } catch (\SoapFault $e) { 147 | var_dump($e->detail); 148 | } catch (\Exception $e) { 149 | var_dump($e->detail); 150 | } 151 | 152 | return collect($r); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/Services/BingAds/Operations/AdGroup.php: -------------------------------------------------------------------------------- 1 | response()->Id ?? 0; 21 | } 22 | 23 | /** 24 | * setId() 25 | * 26 | * @param int $id 27 | * 28 | */ 29 | public function setId($id) { 30 | $this->request()->Id = $id; 31 | return $this; 32 | } 33 | 34 | /** 35 | * getName() 36 | * 37 | * @return string 38 | * 39 | */ 40 | public function getName() { 41 | return $this->response()->Name ?? ''; 42 | } 43 | 44 | /** 45 | * setName() 46 | * 47 | * @param string $name 48 | * 49 | */ 50 | public function setName($name) { 51 | $this->request()->Name = $name; 52 | return $this; 53 | } 54 | 55 | /** 56 | * getCampaignId() 57 | * 58 | * @param int $id 59 | * 60 | */ 61 | public function getCampaignId() { 62 | return $this->request()->CampaignId; 63 | } 64 | 65 | /** 66 | * setCampaignId() 67 | * 68 | * @param int $id 69 | * 70 | */ 71 | public function setCampaignId($id) { 72 | $this->request()->CampaignId = $id; 73 | return $this; 74 | } 75 | 76 | /** 77 | * getStatus() 78 | * 79 | * @reference 80 | * https://github.com/BingAds/BingAds-PHP-SDK/blob/master/src/V12/CampaignManagement/AdGroupStatus.php 81 | * 82 | * @return string 83 | * 84 | */ 85 | public function getStatus() 86 | { 87 | $status = strtoupper($this->response()->Status ?? null); 88 | 89 | switch ($status) { 90 | case 'ACTIVE': return 'ENABLED'; break; 91 | case 'PAUSED': return 'PAUSED'; break; 92 | case 'DELETED': return 'DELETED'; break; 93 | default: 94 | } 95 | 96 | return $status; 97 | } 98 | 99 | /** 100 | * setStatus() 101 | * 102 | * @param string $status 103 | * 104 | */ 105 | public function setStatus($status) 106 | { 107 | $status = ucfirst(strtolower($status)); 108 | if ($status == 'Enabled') { 109 | $status = 'Active'; 110 | } 111 | 112 | if (in_array($status, ['Active','Paused'])) { 113 | $this->request()->Status = $status; 114 | } 115 | 116 | return $this; 117 | } 118 | 119 | /** 120 | * getType() 121 | * 122 | * @return string 123 | * 124 | */ 125 | public function getType() { 126 | return ''; 127 | } 128 | 129 | /** 130 | * getBidStrategy() 131 | * 132 | * @return string 133 | * 134 | */ 135 | public function getBidStrategy() 136 | { 137 | $type = $this->response()->BiddingScheme->InheritedBidStrategyType ?? 'UNKNOWN'; 138 | 139 | switch ($type) { 140 | case 'EnhancedCpc': return 'ECPC'; break; 141 | case 'ManualCpc': return 'CPC'; break; 142 | case 'TargetCpa': return 'CPA'; break; 143 | default: 144 | } 145 | 146 | return $type; 147 | } 148 | 149 | 150 | /** 151 | * getBid() 152 | * 153 | */ 154 | public function getBid() 155 | { 156 | if ($this->getBidStrategy() == 'CPC' || $this->getBidStrategy() == 'ECPC') { 157 | return $this->response()->CpcBid->Amount ?? 0; 158 | } 159 | 160 | if ($this->getBidStrategy() == 'CPA') { 161 | return $this->response()->CpaBid->Amount ?? 0; 162 | } 163 | 164 | return 0; 165 | } 166 | 167 | 168 | /** 169 | * setBid() 170 | * 171 | * https://github.com/BingAds/BingAds-PHP-SDK/blob/dc5c8fb9f9390ab14c102fdff68cb7592091b55c/samples/V12/KeywordsAds.php#L126 172 | * 173 | * @param float $amount 174 | * 175 | */ 176 | public function setBid($amount) 177 | { 178 | if ($this->getBidStrategy() == 'CPC' || $this->getBidStrategy() == 'ECPC') { 179 | $this->request()->CpcBid = new Bid(); 180 | $this->request()->CpcBid->Amount = $amount; 181 | } 182 | 183 | return $this; 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/Services/BingAds/Operations/AdGroupOperations.php: -------------------------------------------------------------------------------- 1 | service = $service; 24 | $this->request = new AdGroupProxy(); 25 | } 26 | 27 | /** 28 | * sendRequest() 29 | * 30 | */ 31 | protected function sendRequest() 32 | { 33 | $serviceCall = $this->service->call(ServiceClientType::CampaignManagementVersion13); 34 | 35 | try { 36 | $adGroup = $this->request(); 37 | 38 | $request = new GetAdGroupsByIdsRequest(); 39 | $request->AccountId = $this->service->getClientId(); 40 | $request->CampaignId = $adGroup->CampaignId; 41 | $request->AdGroupIds = [$adGroup->Id]; 42 | 43 | return $serviceCall->GetService()->GetAdGroupsByIds($request)->AdGroups->AdGroup[0] ?? null; 44 | } catch (\Exception $e) { 45 | print $serviceCall->GetService()->__getLastRequest()."\n"; 46 | print $serviceCall->GetService()->__getLastResponse()."\n"; 47 | } 48 | 49 | return (new AdGroupProxy()); 50 | } 51 | 52 | /** 53 | * save() 54 | * 55 | */ 56 | public function save($updateObject = true) 57 | { 58 | $serviceCall = $this->service->call(ServiceClientType::CampaignManagementVersion13); 59 | 60 | try { 61 | $adGroup = $this->request(); 62 | 63 | $request = new UpdateAdGroupsRequest(); 64 | $request->AccountId = $this->service->getClientId(); 65 | $request->CampaignId = $adGroup->CampaignId; 66 | $request->AdGroups = [$adGroup]; 67 | // $request->UpdateAudienceAdsBidAdjustment = true; 68 | // $request->ReturnInheritedBidStrategyTypes = true; 69 | 70 | $serverResponse = $serviceCall->GetService()->UpdateAdGroups($request); 71 | 72 | // lets update the current object 73 | if ($updateObject) { 74 | $this->get(); 75 | } 76 | } catch (\Exception $e) { 77 | print $serviceCall->GetService()->__getLastRequest()."\n"; 78 | print $serviceCall->GetService()->__getLastResponse()."\n"; 79 | } 80 | 81 | return $this; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/Services/BingAds/Operations/Campaign.php: -------------------------------------------------------------------------------- 1 | response()->Id ?? 0; 22 | } 23 | 24 | /** 25 | * setId() 26 | * 27 | * @param int $id 28 | * 29 | */ 30 | public function setId($id) { 31 | $this->request()->Id = $id; 32 | return $this; 33 | } 34 | 35 | /** 36 | * getName() 37 | * 38 | * @return string 39 | * 40 | */ 41 | public function getName() { 42 | return $this->response()->Name ?? ''; 43 | } 44 | 45 | /** 46 | * setName() 47 | * 48 | * @param string $name 49 | * 50 | */ 51 | public function setName($name) { 52 | $this->request()->Name = $name; 53 | return $this; 54 | } 55 | 56 | /** 57 | * getStatus() 58 | * 59 | * @return string 60 | * 61 | */ 62 | public function getStatus() 63 | { 64 | $status = strtoupper($this->response()->Status ?? 'UNKNOWN'); 65 | 66 | switch ($status) { 67 | case 'ACTIVE': return 'ENABLED'; break; 68 | case 'BUDGETPAUSED': return 'ENABLED'; break; 69 | case 'BUDGETANDMANUALPAUSED': return 'PAUSED'; break; 70 | case 'PAUSED': return 'PAUSED'; break; 71 | case 'REMOVED': return 'DELETED'; break; 72 | default: 73 | } 74 | 75 | return $status; 76 | } 77 | 78 | /** 79 | * setStatus() 80 | * 81 | * @param string $status 82 | * 83 | */ 84 | public function setStatus($status) 85 | { 86 | $status = ucfirst(strtolower($status)); 87 | if ($status == 'Enabled') { 88 | $status = 'Active'; 89 | } 90 | 91 | if (in_array($status, ['Active','Paused'])) { 92 | $this->request()->Status = $status; 93 | } 94 | 95 | return $this; 96 | } 97 | 98 | /** 99 | * getBudget() 100 | * 101 | * @return int 102 | * 103 | */ 104 | public function getBudget() 105 | { 106 | return $this->response()->DailyBudget; 107 | } 108 | 109 | /** 110 | * getBudgetDelivery() 111 | * 112 | * @return string 113 | * 114 | */ 115 | public function getBudgetDelivery() 116 | { 117 | $type = strtoupper($this->response()->BudgetType ?? 'UNKNOWN'); 118 | 119 | switch ($type) { 120 | case 'DAILYBUDGETACCELERATED': return 'ACCELERATED'; break; 121 | case 'DAILYBUDGETSTANDARD': return 'STANDARD'; break; 122 | default: 123 | } 124 | 125 | return $type; 126 | } 127 | 128 | /** 129 | * getChannelType() 130 | * 131 | * @return string 132 | * 133 | */ 134 | public function getChannelType() 135 | { 136 | return strtoupper($this->response()->CampaignType ?? 'UNKNOWN'); 137 | } 138 | 139 | /** 140 | * getBidStrategy() 141 | * 142 | * @return string 143 | * 144 | */ 145 | public function getBidStrategy() 146 | { 147 | $type = strtoupper($this->response()->BiddingScheme->Type ?? 'UNKNOWN'); 148 | 149 | switch ($type) { 150 | case 'ENHANCEDCPC': return 'ECPC'; break; 151 | case 'MANUALCPC': return 'CPC'; break; 152 | case 'TARGETCPA': return 'CPA'; break; 153 | default: 154 | } 155 | 156 | return $type; 157 | } 158 | 159 | /** 160 | * getTargetCpa() 161 | * 162 | * @return int 163 | * 164 | */ 165 | public function getTargetCpa() 166 | { 167 | if ($this->getBidStrategy() == 'CPA') { 168 | return $this->response()->BiddingScheme->TargetCpa ?? 0; 169 | } 170 | 171 | return 0; 172 | } 173 | 174 | 175 | /** 176 | * setTargetCpa() 177 | * 178 | * 179 | */ 180 | public function setTargetCpa($amount = 0) 181 | { 182 | if ($this->getBidStrategy() == 'CPA') { 183 | $biddingScheme = (new TargetCpaBiddingScheme()); 184 | $biddingScheme->Type = 'TargetCpa'; 185 | $biddingScheme->TargetCpa = $amount; 186 | 187 | // why? this is horrible. I hope Bing learns eventually... 188 | $this->request()->BiddingScheme = new SoapVar( 189 | $biddingScheme, 190 | SOAP_ENC_OBJECT, 191 | 'TargetCpaBiddingScheme', 192 | 'https://bingads.microsoft.com/CampaignManagement/v13' 193 | ); 194 | } 195 | 196 | return $this; 197 | } 198 | 199 | /** 200 | * setBudget() 201 | * 202 | * @param int $amount 203 | * 204 | */ 205 | public function setBudget($amount = 0){ 206 | $this->request()->DailyBudget = $amount; 207 | return $this; 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /src/Services/BingAds/Operations/CampaignOperations.php: -------------------------------------------------------------------------------- 1 | service = $service; 27 | $this->request = new CampaignProxy(); 28 | } 29 | 30 | /** 31 | * sendRequest() 32 | * 33 | */ 34 | protected function sendRequest() 35 | { 36 | $serviceCall = $this->service->call(ServiceClientType::CampaignManagementVersion13); 37 | 38 | try { 39 | $campaign = $this->request(); 40 | 41 | $request = new GetCampaignsByIdsRequest(); 42 | $request->AccountId = $this->service->getClientId(); 43 | $request->CampaignIds = [$campaign->Id]; 44 | 45 | return $serviceCall->GetService()->GetCampaignsByIds($request)->Campaigns->Campaign[0] ?? null; 46 | } catch (\Exception $e) { 47 | print $serviceCall->GetService()->__getLastRequest()."\n"; 48 | print $serviceCall->GetService()->__getLastResponse()."\n"; 49 | } 50 | 51 | return (new CampaignProxy()); 52 | } 53 | 54 | /** 55 | * save() 56 | * 57 | * Post your changes to Google Ads Server 58 | * 59 | */ 60 | public function save($updateObject = true) 61 | { 62 | $serviceCall = $this->service->call(ServiceClientType::CampaignManagementVersion13); 63 | 64 | try { 65 | $campaign = $this->request(); 66 | 67 | $request = new UpdateCampaignsRequest(); 68 | $request->AccountId = $this->service->getClientId(); 69 | $request->CampaignId = $campaign->Id; 70 | $request->Campaigns = [$campaign]; 71 | // $request->UpdateAudienceAdsBidAdjustment = true; 72 | // $request->ReturnInheritedBidStrategyTypes = true; 73 | 74 | $serverResponse = $serviceCall->GetService()->UpdateCampaigns($request); 75 | 76 | // lets update the current object 77 | if ($updateObject) { 78 | $this->get(); 79 | } 80 | } catch (\Exception $e) { 81 | print $serviceCall->GetService()->__getLastRequest()."\n"; 82 | print $serviceCall->GetService()->__getLastResponse()."\n"; 83 | } 84 | 85 | return $this; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/Services/BingAds/Operations/Customer.php: -------------------------------------------------------------------------------- 1 | response()->Id ?? 0; 15 | } 16 | 17 | /** 18 | * setId() 19 | * 20 | * @param int $id 21 | * 22 | */ 23 | public function setId($id) { 24 | $this->request()->Id = $id; 25 | return $this; 26 | } 27 | 28 | /** 29 | * getName() 30 | * 31 | * @return string 32 | * 33 | */ 34 | public function getName() { 35 | return $this->response()->Name ?? ''; 36 | } 37 | 38 | /** 39 | * setName() 40 | * 41 | * @param string $name 42 | * 43 | */ 44 | public function setName($name) { 45 | $this->request()->Name = $name; 46 | return $this; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Services/BingAds/Operations/CustomerOperations.php: -------------------------------------------------------------------------------- 1 | service = $service; 22 | $this->request = new CustomerProxy(); 23 | } 24 | 25 | /** 26 | * sendRequest() 27 | * 28 | */ 29 | protected function sendRequest() 30 | { 31 | $serviceCall = $this->service->call(ServiceClientType::CustomerManagementVersion13); 32 | 33 | try { 34 | $customer = $this->request(); 35 | 36 | $request = new GetCustomersInfoRequest(); 37 | $request->CustomerNameFilter = ''; 38 | $request->TopN = 100; 39 | 40 | return $serviceCall->GetService()->GetCustomersInfo($request)->CustomersInfo->CustomerInfo[0] ?? null; 41 | } catch (\Exception $e) { 42 | print $serviceCall->GetService()->__getLastRequest()."\n"; 43 | print $serviceCall->GetService()->__getLastResponse()."\n"; 44 | } 45 | 46 | return (new CustomerProxy()); 47 | } 48 | 49 | /** 50 | * save() 51 | * 52 | * TODO: this could probably be removed, 53 | * but just incase someone calls this... 54 | */ 55 | public function save($updateObject = true) { 56 | return $this; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Services/BingAds/Operations/OfflineConversions.php: -------------------------------------------------------------------------------- 1 | service = $service; 44 | } 45 | 46 | /** 47 | * getConversions() 48 | * 49 | * @return array 50 | */ 51 | public function getConversions() 52 | { 53 | return $this->offlineConversions; 54 | } 55 | 56 | /** 57 | * addBulk() 58 | * 59 | * @return OfflineConversions 60 | */ 61 | public function addBulk(array $conversions = []) 62 | { 63 | foreach ($conversions as $conversion) { 64 | $this->add($conversion); 65 | } 66 | 67 | return $this; 68 | } 69 | 70 | /** 71 | * add() 72 | * 73 | * @return OfflineConversions 74 | */ 75 | public function add(array $conversions = []) 76 | { 77 | $gc = new OfflineConversion(); 78 | $gc->ConversionName = $conversions['name']; 79 | $gc->ConversionTime = $conversions['time']; 80 | $gc->ConversionValue = $conversions['value']; 81 | $gc->MicrosoftClickId = $conversions['click_id']; 82 | 83 | $this->offlineConversions[] = $conversions; 84 | $this->mutations[] = $gc; 85 | 86 | return $this; 87 | } 88 | 89 | /** 90 | * upload() 91 | * 92 | */ 93 | public function upload($outputValue = false) 94 | { 95 | $errorResponse = []; 96 | $successResponse = []; 97 | 98 | foreach ($this->mutations as $i=>$mutate) { 99 | $click = $this->offlineConversions[$i] ?? []; 100 | 101 | try { 102 | $serviceCall = $this->service->call(ServiceClientType::CampaignManagementVersion13); 103 | 104 | $request = new ApplyOfflineConversionsRequest(); 105 | $request->OfflineConversions = [$mutate]; 106 | 107 | $result = $serviceCall->GetService()->ApplyOfflineConversions($request); 108 | 109 | if (isset($result->PartialErrors->BatchError)) { 110 | $errorResponse[$i] = [ 111 | 'name' => $click['name'] ?? '', 112 | 'time' => $click['time'] ?? '', 113 | 'click_id' => $click['click_id'], 114 | 'value' => $click['value'] ?? 0, 115 | 'error' => $result->PartialErrors->BatchError[0]->ErrorCode ?? 'unknown' 116 | ]; 117 | } else { 118 | if ($outputValue==true) { 119 | $successResponse[$i] = [ 120 | 'name' => $click['name'], 121 | 'time' => $click['time'], 122 | 'click_id' => $click['click_id'], 123 | 'value' => $click['value'] ?? 0, 124 | ]; 125 | } else { 126 | $successResponse[$i] = $click['click_id']; 127 | } 128 | } 129 | } catch (SoapFault $e) { 130 | // printf("-----\r\nFault Code: %s\r\nFault String: %s\r\nFault Detail: \r\n", $e->faultcode, $e->faultstring); 131 | // var_dump($e->detail); 132 | 133 | // display generic error code 134 | $errorCode = $e->faultcode; 135 | 136 | // display auth error 137 | if (isset($e->detail->AdApiFaultDetail->Errors->AdApiError->ErrorCode)) { 138 | $errorCode = $e->detail->AdApiFaultDetail->Errors->AdApiError->ErrorCode; 139 | } 140 | 141 | if (isset($e->detail->ApiFaultDetail->OperationErrors->OperationError->ErrorCode)) { 142 | $errorCode = $e->detail->ApiFaultDetail->OperationErrors->OperationError->ErrorCode; 143 | } 144 | 145 | $errorResponse[$i] = [ 146 | 'name' => $click['name'] ?? '', 147 | 'time' => $click['time'] ?? '', 148 | 'click_id' => $click['click_id'], 149 | 'value' => $click['value'] ?? 0, 150 | 'error' => $errorCode 151 | ]; 152 | 153 | // print "-----\r\nLast SOAP request/response:\r\n"; 154 | // print $serviceCall->GetWsdl() . "\r\n"; 155 | // print $serviceCall->GetService()->__getLastRequest()."\r\n"; 156 | // print $serviceCall->GetService()->__getLastResponse()."\r\n"; 157 | } catch (Exception $e) { 158 | $errorResponse[$i] = [ 159 | 'name' => $click['name'] ?? '', 160 | 'time' => $click['time'] ?? '', 161 | 'click_id' => $click['click_id'], 162 | 'value' => $click['value'] ?? 0, 163 | 'error' => $e->getMessage() 164 | ]; 165 | } 166 | } 167 | 168 | // prevent abuse in api requests 169 | // default is 0.05 seconds per request 170 | usleep(050000); 171 | 172 | return [ 173 | 'errors' => ($errorResponse) ? $errorResponse : false, 174 | 'success' => ($successResponse) ? $successResponse : false 175 | ]; 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/Services/BingAds/Operations/Operation.php: -------------------------------------------------------------------------------- 1 | request; 31 | } 32 | 33 | /** 34 | * response() 35 | * 36 | */ 37 | public function response() { 38 | return $this->response; 39 | } 40 | 41 | /** 42 | * set() 43 | * 44 | */ 45 | public function set($entity) { 46 | $this->response = $entity; 47 | // set up our request if we have not done this yet 48 | $this->request()->Id = $entity->Id; 49 | return $this; 50 | } 51 | 52 | /** 53 | * get() 54 | * 55 | */ 56 | public function get() { 57 | $this->set($this->sendRequest()); 58 | return $this; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Services/BingAds/ReportDownload.php: -------------------------------------------------------------------------------- 1 | serviceProxy = $serviceProxy; 32 | 33 | $waitTime = 10 * 1; 34 | $reportRequestStatus = null; 35 | $reportName = time(); 36 | $DownloadPath = storage_path("app/".$reportName.'.zip'); 37 | 38 | // This sample polls every 30 seconds up to 5 minutes. 39 | // In production you may poll the status every 1 to 2 minutes for up to one hour. 40 | // If the call succeeds, stop polling. If the call or 41 | // download fails, the call throws a fault. 42 | 43 | for ($i = 0; $i < 120; $i++) { 44 | sleep($waitTime); 45 | 46 | $reportRequestStatus = $this->pollGenerateReport($this->serviceProxy, $reportId)->ReportRequestStatus; 47 | 48 | if ($reportRequestStatus->Status == ReportRequestStatusType::Success || 49 | $reportRequestStatus->Status == ReportRequestStatusType::Error) { 50 | break; 51 | } 52 | } 53 | 54 | if ($reportRequestStatus != null) { 55 | if ($reportRequestStatus->Status == ReportRequestStatusType::Success) { 56 | $reportDownloadUrl = $reportRequestStatus->ReportDownloadUrl; 57 | 58 | if ($reportDownloadUrl == null) { 59 | print "No report data for the submitted request\n"; 60 | 61 | $this->results = null; 62 | 63 | return; 64 | } else { 65 | $this->downloadFile($reportDownloadUrl, $DownloadPath); 66 | } 67 | } elseif ($reportRequestStatus->Status == ReportRequestStatusType::Error) { 68 | printf("The request failed. Try requesting the report " . 69 | "later.\nIf the request continues to fail, contact support.\n"); 70 | 71 | $this->results = null; 72 | 73 | return; 74 | } else { 75 | printf( 76 | "The request is taking longer than expected.\n " . 77 | "Save the report ID (%s) and try again later.\n", 78 | $reportId 79 | ); 80 | 81 | $this->results = null; 82 | 83 | return; 84 | } 85 | } 86 | 87 | $this->results = $this->extractZip($DownloadPath, $reportId); 88 | } 89 | 90 | 91 | protected function downloadFile($reportDownloadUrl, $downloadPath) 92 | { 93 | if (!$reader = fopen($reportDownloadUrl, 'rb')) { 94 | throw new Exception("Failed to open URL " . $reportDownloadUrl . "."); 95 | } 96 | 97 | if (!$writer = fopen($downloadPath, 'wb')) { 98 | fclose($reader); 99 | throw new Exception("Failed to create ZIP file " . $downloadPath . "."); 100 | } 101 | 102 | $bufferSize = 100 * 1024; 103 | while (!feof($reader)) { 104 | if (false === ($buffer = fread($reader, $bufferSize))) { 105 | fclose($reader); 106 | fclose($writer); 107 | throw new Exception("Read operation from URL failed."); 108 | } 109 | 110 | if (fwrite($writer, $buffer) === false) { 111 | fclose($reader); 112 | fclose($writer); 113 | $exception = new Exception("Write operation to ZIP file failed."); 114 | } 115 | } 116 | 117 | fclose($reader); 118 | fflush($writer); 119 | fclose($writer); 120 | } 121 | 122 | protected function extractZip($location, $name) 123 | { 124 | $zip = new ZipArchive; 125 | if ($zip->open($location) === true) { 126 | $zip->extractTo(storage_path('app/')); 127 | $zip->close(); 128 | 129 | unlink($location); 130 | } 131 | 132 | // Sometimes the report id has a extension starting with _ 133 | // if this occurs we will just remove it. 134 | $fileNameLength = strpos($name, '_') > 0 ? strpos($name, '_') : strlen($name); 135 | $filePath = storage_path('app/') . substr($name, 0, $fileNameLength) . '.csv'; 136 | 137 | try { 138 | $data = file($filePath); 139 | } finally { 140 | unlink($filePath); 141 | } 142 | 143 | return $data; 144 | } 145 | 146 | 147 | protected function pollGenerateReport($session, $reportRequestId) 148 | { 149 | $request = new PollGenerateReportRequest(); 150 | $request->ReportRequestId = $reportRequestId; 151 | 152 | return $session->GetService()->PollGenerateReport($request); 153 | } 154 | 155 | 156 | /** 157 | * toString() 158 | * 159 | * 160 | * @return string results 161 | */ 162 | public function toString() 163 | { 164 | return $this->results ?? ''; 165 | } 166 | 167 | /** 168 | * aggregate() 169 | * 170 | * 171 | * @return collection results 172 | */ 173 | public function aggregate($field) 174 | { 175 | $results = $this->toArray(); 176 | 177 | $only = [ 178 | 'impressions', 179 | 'clicks', 180 | 'cost', 181 | 'conversions', 182 | 'conversion_value', 183 | ]; 184 | 185 | $r = []; 186 | foreach ($results as $key => $value) { 187 | unset($value['date']); 188 | 189 | if (isset($r[$value[$field]])) { 190 | $x = $r[$value[$field]]; 191 | 192 | foreach ($value as $k => $v) { 193 | if (!in_array($k, $only)) { 194 | continue; 195 | } 196 | 197 | $n = $x[$k]; 198 | 199 | if (!is_numeric($n)) { 200 | continue 2; 201 | } 202 | 203 | if (!is_numeric($v)) { 204 | continue 2; 205 | } 206 | 207 | $value[$k] = $v+$n; 208 | } 209 | 210 | $r[$value[$field]] = $value; 211 | } else { 212 | $r[$value[$field]] = $value; 213 | } 214 | } 215 | 216 | return collect($r); 217 | } 218 | 219 | /** 220 | * toArray() 221 | * 222 | * 223 | * @return array results 224 | */ 225 | public function toArray() 226 | { 227 | if (!$this->results) { 228 | return []; 229 | } 230 | 231 | $csv = array_map('str_getcsv', $this->results); 232 | 233 | $h = $csv[10] ?? []; 234 | 235 | $header = []; 236 | foreach ($h as $label) { 237 | $label = strtolower($label); 238 | 239 | switch ($label) { 240 | case 'timeperiod': $label = 'date'; break; 241 | case 'accountid': $label = 'account_id'; break; 242 | case 'accountname': $label = 'account_name'; break; 243 | case 'campaignid': $label = 'campaign_id'; break; 244 | case 'campaignname': $label = 'campaign_name'; break; 245 | case 'campaignstatus': $label = 'campaign_status'; break; 246 | case 'adgroupid': $label = 'ad_group_id'; break; 247 | case 'adgroupname': $label = 'ad_group_name'; break; 248 | case 'spend': $label = 'cost'; break; 249 | case 'revenue': $label = 'conversion_value'; break; 250 | case 'averageposition': $label = 'avg_position'; break; 251 | case 'destinationurl': $label = 'destination_url'; break; 252 | case 'finalurl': $label = 'final_url'; break; 253 | case 'gender': $label = 'gender'; break; 254 | case 'agegroup': $label = 'age_range'; break; 255 | case 'searchquery': $label = 'search_term'; break; 256 | case 'locationtype': $label = 'location_type'; break; 257 | case 'mostspecificlocation': $label = 'location'; break; 258 | case 'metroarea': $label = 'metro_area'; break; 259 | case 'postalcode': $label = 'postal_code'; break; 260 | case 'locationid': $label = 'location_id'; break; 261 | default: 262 | } 263 | 264 | $header[] = str_replace(' ', '_', $label); 265 | } 266 | 267 | // wtf is this bing?? 268 | unset($csv[0]); 269 | unset($csv[1]); 270 | unset($csv[2]); 271 | unset($csv[3]); 272 | unset($csv[4]); 273 | unset($csv[5]); 274 | unset($csv[6]); 275 | unset($csv[7]); 276 | unset($csv[8]); 277 | unset($csv[9]); 278 | unset($csv[10]); 279 | 280 | // more wtf rows... 281 | array_pop($csv); 282 | array_pop($csv); 283 | 284 | $report = []; 285 | foreach ($csv as $index => $columns) { 286 | $r = []; 287 | foreach ($columns as $index2=>$cs) { 288 | if (!isset($header[$index2])) { 289 | continue; 290 | } 291 | 292 | $n = $header[$index2]; 293 | 294 | $r[$n] = str_replace(',', '', $cs); 295 | } 296 | 297 | $report[] = $r; 298 | } 299 | 300 | ksort($report); 301 | 302 | return $report; 303 | } 304 | 305 | 306 | /** 307 | * toCollection() 308 | * 309 | * 310 | * @return \Illuminate\Support\Collection results 311 | */ 312 | public function toCollection() 313 | { 314 | return collect($this->toArray()); 315 | } 316 | } 317 | -------------------------------------------------------------------------------- /src/Services/BingAds/Service.php: -------------------------------------------------------------------------------- 1 | clientId = $clientId; 69 | return $this; 70 | } 71 | 72 | /** 73 | * withCustomerId() 74 | * 75 | * Sets the customer id 76 | * 77 | * @return self 78 | */ 79 | public function withCustomerId($customerId) { 80 | $this->customerId = $customerId; 81 | return $this; 82 | } 83 | 84 | /** 85 | * getClientId() 86 | * 87 | * @return string 88 | */ 89 | public function getClientId() { 90 | return $this->clientId; 91 | } 92 | 93 | /** 94 | * getCustomerId() 95 | * 96 | * @return string 97 | */ 98 | public function getCustomerId() { 99 | return $this->customerId; 100 | } 101 | 102 | /** 103 | * setEnvironment() 104 | * 105 | * Sets the Bing API environment 106 | * 107 | * @return self 108 | */ 109 | public function setEnvironment($env) { 110 | $this->environment = $env; 111 | return $this; 112 | } 113 | 114 | /** 115 | * getEnvironment() 116 | * 117 | * Get the current Bing API environment 118 | * 119 | * @return string 120 | */ 121 | public function getEnvironment() { 122 | return $this->environment; 123 | } 124 | 125 | /** 126 | * fetch() 127 | * 128 | * 129 | */ 130 | public function fetch() { 131 | return (new Fetch($this)); 132 | } 133 | 134 | /** 135 | * call() 136 | * 137 | * 138 | */ 139 | public function call($service) { 140 | $serviceClient = (new ServiceClient($service, $this->session(), $this->environment)); 141 | $serviceClient->SetAuthorizationData($this->session()); 142 | return $serviceClient; 143 | } 144 | 145 | /** 146 | * reports() 147 | * 148 | * 149 | */ 150 | public function reports($dateFrom, $dateTo) { 151 | return (new Reports($this))->setDateRange($dateFrom, $dateTo); 152 | } 153 | 154 | /** 155 | * offlineConversionImport() 156 | * 157 | * 158 | */ 159 | public function offlineConversionImport(array $conversions = []) { 160 | return (new OfflineConversions($this))->addBulk($conversions); 161 | } 162 | 163 | /** 164 | * adGroup() 165 | * 166 | * 167 | * @return AdGroupOperation 168 | */ 169 | public function adGroup($adGroup, $campaignId = null) 170 | { 171 | if ($adGroup instanceof \stdClass) { 172 | return (new AdGroup($this))->set($adGroup); 173 | } 174 | 175 | return (new AdGroup($this))->setId($adGroup)->setCampaignId($campaignId)->get(); 176 | } 177 | 178 | /** 179 | * campaign() 180 | * 181 | * @return Campaign 182 | */ 183 | public function campaign($campaign) 184 | { 185 | if ($campaign instanceof \stdClass) { 186 | return (new Campaign($this))->set($campaign); 187 | } 188 | 189 | return (new Campaign($this))->setId($campaign)->get(); 190 | } 191 | 192 | /** 193 | * campaign() 194 | * 195 | * @return Customer 196 | */ 197 | public function customer($customer) 198 | { 199 | if ($customer instanceof \stdClass) { 200 | return (new Customer($this))->set($customer); 201 | } 202 | 203 | return (new Customer($this))->setId($customer)->get(); 204 | } 205 | 206 | /** 207 | * Configuration 208 | * 209 | * @return Configuration 210 | */ 211 | public function configuration($config = []) 212 | { 213 | if (empty($config)) { 214 | // use laravel config 215 | $config = config('bing-ads'); 216 | 217 | // check if config already exist 218 | if ($this->config) { 219 | return $this->config; 220 | } 221 | } 222 | 223 | // create a new config 224 | return ($this->config = (($config))); 225 | } 226 | 227 | /** 228 | * session() 229 | * 230 | * 231 | */ 232 | public function session() 233 | { 234 | if (!$this->session) { 235 | $config = $this->configuration(); 236 | 237 | $AuthorizationData = (new AuthorizationData()) 238 | ->withAccountId($this->getClientId()) 239 | ->withAuthentication($this->oAuthcredentials($config)) 240 | ->withDeveloperToken($config['developerToken']); 241 | 242 | // Add Customer Id (OPTIONAL) 243 | if ($this->getCustomerId()) { 244 | $AuthorizationData->withCustomerId($this->getCustomerId()); 245 | } 246 | 247 | try { 248 | $AuthorizationData->Authentication->RequestOAuthTokensByRefreshToken($config['refreshToken']); 249 | } catch (OAuthTokenRequestException $e) { 250 | // printf("Error: %s\n", $e->Error); 251 | // printf("Description: %s\n", $e->Description); 252 | // AuthHelper::RequestUserConsent(); 253 | } 254 | 255 | $this->session = $AuthorizationData; 256 | } 257 | 258 | return $this->session; 259 | } 260 | 261 | /** 262 | * oAuth2credentials() 263 | * 264 | */ 265 | protected function oAuthcredentials($config) 266 | { 267 | return (new OAuthDesktopMobileAuthCodeGrant()) 268 | ->withClientSecret($config['clientSecret']) 269 | ->withClientId($config['clientId']); 270 | } 271 | } 272 | -------------------------------------------------------------------------------- /src/Services/FacebookAds/Fetch.php: -------------------------------------------------------------------------------- 1 | service = $service; 25 | } 26 | 27 | /** 28 | * getCampaigns() 29 | * 30 | * @reference 31 | * https://developers.facebook.com/docs/marketing-api/reference/ad-account/campaigns/ 32 | * 33 | * allowed fields 34 | * https://github.com/facebook/facebook-php-business-sdk/blob/master/src/FacebookAds/Object/Fields/CampaignFields.php 35 | * 36 | * @param array $fields 37 | * @param array $params 38 | * @return object Collection 39 | */ 40 | public function getCampaigns($fields = [], $params = []) 41 | { 42 | // set the default fields if none are set 43 | if (!$fields) { 44 | $fields = [ 45 | CampaignFields::ACCOUNT_ID, 46 | CampaignFields::ID, 47 | CampaignFields::NAME, 48 | CampaignFields::STATUS, 49 | CampaignFields::BID_STRATEGY, 50 | CampaignFields::DAILY_BUDGET 51 | ]; 52 | } 53 | 54 | $account = new AdAccount('act_'.$this->service->getAccountId()); 55 | $campaigns = $account->getCampaigns($fields, $params); 56 | $data = $campaigns->getResponse()->getContent()['data'] ?? []; 57 | 58 | return collect($data); 59 | } 60 | 61 | /** 62 | * getAdSets() 63 | * This is here to be compatible with google/bing 64 | * 65 | * @param array $fields 66 | * @param array $params 67 | * @return object Collection 68 | */ 69 | public function getAdSets($fields = [], $params = []) { 70 | return $this->getAdGroups($fields, $params); 71 | } 72 | 73 | /** 74 | * getAdGroups() 75 | * 76 | * @reference 77 | * https://developers.facebook.com/docs/marketing-api/reference/ad-account/adsets/ 78 | * 79 | * allowed fields 80 | * https://github.com/facebook/facebook-php-business-sdk/blob/master/src/FacebookAds/Object/Fields/AdSetFields.php 81 | * 82 | * @param array $fields 83 | * @param array $params 84 | * @return object Collection 85 | */ 86 | public function getAdGroups($fields = [], $params = []) 87 | { 88 | // set the default fields if none are set 89 | if (!$fields) { 90 | $fields = [ 91 | AdSetFields::ACCOUNT_ID, 92 | AdSetFields::CAMPAIGN_ID, 93 | AdSetFields::ID, 94 | AdSetFields::NAME, 95 | AdSetFields::STATUS, 96 | AdSetFields::DAILY_BUDGET, 97 | AdSetFields::BID_AMOUNT, 98 | AdSetFields::BID_STRATEGY, 99 | ]; 100 | } 101 | 102 | $account = new AdAccount('act_'.$this->service->getAccountId()); 103 | $adsets = $account->getAdSets($fields, $params); 104 | $data = $adsets->getResponse()->getContent()['data'] ?? []; 105 | 106 | return collect($data); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/Services/FacebookAds/Reports.php: -------------------------------------------------------------------------------- 1 | service = $service; 43 | } 44 | 45 | /** 46 | * setDateRange() 47 | * 48 | * 49 | * @return self 50 | */ 51 | public function setDateRange($dateFrom='', $dateTo='') { 52 | $this->dateRange[] = $dateFrom; 53 | $this->dateRange[] = $dateTo; 54 | return $this; 55 | } 56 | 57 | /** 58 | * setFields() 59 | * 60 | * 61 | * @return self 62 | */ 63 | public function setFields($fields = []) { 64 | $this->fields = $fields; 65 | return $this; 66 | } 67 | 68 | /** 69 | * setParams() 70 | * 71 | * @reference https://developers.facebook.com/docs/marketing-api/insights/parameters/v11.0 72 | * @return self 73 | */ 74 | public function setParams($params = []) { 75 | $this->params = $params; 76 | return $this; 77 | } 78 | 79 | /** 80 | * getAccountReport() 81 | * 82 | * @reference 83 | * https://developers.facebook.com/docs/marketing-api/insights 84 | * 85 | */ 86 | public function getAccountReport() 87 | { 88 | $api = new AdAccount('act_'.$this->service->getAccountId()); 89 | 90 | $defaultParams = [ 91 | 'level' => 'account', 92 | 'time_range' => [ 93 | 'since' => $this->dateRange[0], 94 | 'until' => $this->dateRange[1], 95 | ] 96 | ]; 97 | 98 | $params = array_merge($defaultParams, $this->params); 99 | 100 | $fields = [ 101 | AdsInsightsFields::ACCOUNT_ID, 102 | AdsInsightsFields::ACCOUNT_NAME, 103 | AdsInsightsFields::IMPRESSIONS, 104 | AdsInsightsFields::CLICKS, 105 | AdsInsightsFields::CTR, 106 | AdsInsightsFields::CONVERSIONS, 107 | AdsInsightsFields::SPEND 108 | ]; 109 | 110 | if (!empty($this->fields)) { 111 | $fields = $this->fields; 112 | } 113 | 114 | $insights = $api->getInsights($fields, $params); 115 | return collect($insights->getResponse()->getContent()['data'] ?? []); 116 | } 117 | 118 | 119 | /** 120 | * getCampaignReport() 121 | * 122 | * @reference 123 | * https://developers.facebook.com/docs/marketing-api/insights 124 | * https://developers.facebook.com/docs/marketing-api/reference/ad-campaign-group/insights 125 | * 126 | */ 127 | public function getCampaignReport() 128 | { 129 | $api = new AdAccount('act_'.$this->service->getAccountId()); 130 | 131 | $defaultParams = [ 132 | 'level' => 'campaign', 133 | 'time_range' => [ 134 | 'since' => $this->dateRange[0], 135 | 'until' => $this->dateRange[1], 136 | ] 137 | ]; 138 | 139 | $params = array_merge($defaultParams, $this->params); 140 | 141 | $fields = [ 142 | AdsInsightsFields::ACCOUNT_ID, 143 | AdsInsightsFields::CAMPAIGN_ID, 144 | AdsInsightsFields::CAMPAIGN_NAME, 145 | AdsInsightsFields::IMPRESSIONS, 146 | AdsInsightsFields::CLICKS, 147 | AdsInsightsFields::CTR, 148 | AdsInsightsFields::CONVERSIONS, 149 | AdsInsightsFields::SPEND 150 | ]; 151 | 152 | if (!empty($this->fields)) { 153 | $fields = $this->fields; 154 | } 155 | 156 | $insights = $api->getInsights($fields, $params); 157 | return collect($insights->getResponse()->getContent()['data'] ?? []); 158 | } 159 | 160 | /** 161 | * getAdSetReport() 162 | * This is here to be compatible with google/bing 163 | * 164 | */ 165 | public function getAdSetReport() { 166 | return $this->getAdGroupReport(); 167 | } 168 | 169 | /** 170 | * getAdGroupReport() 171 | * 172 | * @reference https://developers.facebook.com/docs/marketing-api/insights/parameters/v11.0 173 | * 174 | */ 175 | public function getAdGroupReport() 176 | { 177 | $api = new AdAccount('act_'.$this->service->getAccountId()); 178 | 179 | $defaultParams = [ 180 | 'level' => 'adset', 181 | 'time_range' => [ 182 | 'since' => $this->dateRange[0], 183 | 'until' => $this->dateRange[1], 184 | ] 185 | ]; 186 | 187 | $params = array_merge($defaultParams, $this->params); 188 | 189 | $fields = [ 190 | AdsInsightsFields::ACCOUNT_ID, 191 | AdsInsightsFields::CAMPAIGN_ID, 192 | AdsInsightsFields::ADSET_ID, 193 | AdsInsightsFields::ADSET_NAME, 194 | AdsInsightsFields::IMPRESSIONS, 195 | AdsInsightsFields::CLICKS, 196 | AdsInsightsFields::CTR, 197 | AdsInsightsFields::CONVERSIONS, 198 | AdsInsightsFields::SPEND 199 | ]; 200 | 201 | if (!empty($this->fields)) { 202 | $fields = $this->fields; 203 | } 204 | 205 | $insights = $api->getInsights($fields, $params); 206 | return collect($insights->getResponse()->getContent()['data'] ?? []); 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /src/Services/FacebookAds/Service.php: -------------------------------------------------------------------------------- 1 | session(); 45 | } 46 | 47 | /** 48 | * with() 49 | * 50 | * Sets the client ids 51 | * 52 | * @return self 53 | */ 54 | public function with($accountId) { 55 | $this->accountId = $accountId; 56 | return $this; 57 | } 58 | 59 | /** 60 | * getClientId() 61 | * This is here to be compatible with google/bing 62 | * 63 | * @return $accountId 64 | */ 65 | public function getClientId() { 66 | return $this->getAccountId(); 67 | } 68 | 69 | /** 70 | * getAccountId() 71 | * 72 | * @return $accountId 73 | */ 74 | public function getAccountId() { 75 | return $this->accountId; 76 | } 77 | 78 | /** 79 | * fetch() 80 | * 81 | * 82 | */ 83 | public function fetch() { 84 | return (new Fetch($this)); 85 | } 86 | 87 | /** 88 | * reports() 89 | * 90 | * 91 | */ 92 | public function reports($dateFrom, $dateTo) { 93 | return (new Reports($this))->setDateRange($dateFrom, $dateTo); 94 | } 95 | 96 | /** 97 | * adGroup() 98 | * This is here to be compatible with google/bing 99 | * 100 | * @return AdGroup 101 | */ 102 | public function adSet($adGroup) { 103 | return $this->adGroup($adGroup); 104 | } 105 | 106 | /** 107 | * adGroup() 108 | * 109 | * @return AdGroup 110 | */ 111 | public function adGroup($adGroup) 112 | { 113 | // TODO: build out single adgroup (adset) objects 114 | } 115 | 116 | /** 117 | * campaign() 118 | * 119 | * @return Campaign 120 | */ 121 | public function campaign($campaign) 122 | { 123 | // TODO: build out single campaign 124 | } 125 | 126 | /** 127 | * session() 128 | * 129 | * Sets the facebook instance 130 | * 131 | * @return object $this->session 132 | */ 133 | public function session() { 134 | $config = $this->configuration(); 135 | Api::init($config['app_id'], $config['app_secret'], $config['access_token']); 136 | $this->session = Api::instance(); 137 | return $this->session; 138 | } 139 | 140 | /** 141 | * Configuration 142 | * 143 | * @return Configuration 144 | */ 145 | public function configuration($config = []) 146 | { 147 | if (empty($config)) { 148 | // use laravel config 149 | $config = config('facebook-ads'); 150 | 151 | // check if config already exist 152 | if ($this->config) { 153 | return $this->config; 154 | } 155 | } 156 | 157 | // create a new config 158 | return ($this->config = (($config))); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/Services/GoogleAds/Fetch.php: -------------------------------------------------------------------------------- 1 | service = $service; 31 | } 32 | 33 | /** 34 | * getCampaigns() 35 | * 36 | * @reference 37 | * https://github.com/googleads/googleads-php-lib/blob/master/src/Google/AdsApi/AdWords/v201809/cm/Campaign.php 38 | * https://developers.google.com/adwords/api/docs/reference/v201809/CampaignService.Campaign 39 | * https://developers.google.com/adwords/api/docs/appendix/selectorfields 40 | * https://github.com/googleads/googleads-php-lib/blob/master/examples/AdWords/v201809/BasicOperations/GetCampaigns.php 41 | * 42 | * @return object Collection 43 | */ 44 | public function getCampaigns($rArray = true, $filters = []) 45 | { 46 | $selector = new Selector(); 47 | $selector->setPaging(new Paging(0, 5000)); 48 | $selector->setFields([ 49 | 'Id', 50 | 'Name', 51 | 'Amount', 52 | 'CampaignStatus', 53 | 'BiddingStrategyType', 54 | 'AdvertisingChannelType', 55 | 'TargetCpa' 56 | ]); 57 | 58 | if ($filters) { 59 | $predicates = []; 60 | 61 | foreach ($filters as $key=>$values) { 62 | $predicates[] = new Predicate($key, PredicateOperator::IN, $values); 63 | } 64 | 65 | $selector->setPredicates($predicates); 66 | } 67 | 68 | $totalNumEntries = 0; 69 | $entries = []; 70 | 71 | do { 72 | $page = $this->service->call(CampaignService::class)->get($selector); 73 | $items = $page->getEntries(); 74 | 75 | if ($page->getEntries() !== null) { 76 | $totalNumEntries = $page->getTotalNumEntries(); 77 | 78 | foreach ($items as $item) { 79 | $campaign = $this->service->campaign($item); 80 | 81 | if ($rArray) { 82 | $entries[] = [ 83 | 'id' => $campaign->getId(), 84 | 'name' => $campaign->getName(), 85 | 'status' => $campaign->getStatus(), 86 | 'channel' => $campaign->getChannelType(), 87 | 'budget' => $campaign->getBudget(), 88 | 'bid_strategy' => $campaign->getBidStrategy(), 89 | 'target_cpa' => $campaign->getTargetCpa() 90 | ]; 91 | } else { 92 | $entries[] = $campaign; 93 | } 94 | } 95 | } 96 | 97 | $selector->getPaging()->setStartIndex( 98 | $selector->getPaging()->getStartIndex() + 5000 99 | ); 100 | } while ($selector->getPaging()->getStartIndex() < $totalNumEntries); 101 | 102 | return collect($entries); 103 | } 104 | 105 | 106 | 107 | /** 108 | * getAdGroups() 109 | * 110 | * @reference 111 | * https://github.com/googleads/googleads-php-lib/blob/master/src/Google/AdsApi/AdWords/v201809/cm/AdGroup.php 112 | * https://developers.google.com/adwords/api/docs/reference/v201809/AdGroupService.AdGroup 113 | * https://github.com/googleads/googleads-php-lib/blob/master/examples/AdWords/v201809/BasicOperations/GetCampaigns.php 114 | * 115 | * @return object Collection 116 | */ 117 | public function getAdGroups($rArray = true, $filters = []) 118 | { 119 | $selector = new Selector(); 120 | $selector->setPaging(new Paging(0, 5000)); 121 | $selector->setFields([ 122 | 'Id', 123 | 'Name', 124 | 'CampaignId', 125 | 'Status', 126 | 'BiddingStrategyType', 127 | 'EnhancedCpcEnabled', 128 | 'AdGroupType', 129 | 'CpcBid', 130 | 'CpmBid', 131 | 'TargetCpaBid' 132 | ]); 133 | 134 | if ($filters) { 135 | $predicates = []; 136 | 137 | foreach ($filters as $key=>$values) { 138 | $predicates[] = new Predicate($key, PredicateOperator::IN, $values); 139 | } 140 | 141 | $selector->setPredicates($predicates); 142 | } 143 | 144 | $totalNumEntries = 0; 145 | $entries = []; 146 | 147 | do { 148 | $page = $this->service->call(AdGroupService::class)->get($selector); 149 | $items = $page->getEntries(); 150 | 151 | if ($page->getEntries() !== null) { 152 | $totalNumEntries = $page->getTotalNumEntries(); 153 | 154 | foreach ($items as $item) { 155 | $adgroup = $this->service->adGroup($item); 156 | 157 | if ($rArray) { 158 | $entries[] = [ 159 | 'id' => $adgroup->getId(), 160 | 'name' => $adgroup->getName(), 161 | 'status' => $adgroup->getStatus(), 162 | 'campaign_id' => $adgroup->getCampaignId(), 163 | 'type' => $adgroup->getType(), 164 | 'bid_strategy' => $adgroup->getBidStrategy(), 165 | 'bid' => $adgroup->getBid() 166 | ]; 167 | } else { 168 | $entries[] = $adgroup; 169 | } 170 | } 171 | } 172 | 173 | $selector->getPaging()->setStartIndex( 174 | $selector->getPaging()->getStartIndex() + 5000 175 | ); 176 | } while ($selector->getPaging()->getStartIndex() < $totalNumEntries); 177 | 178 | return collect($entries); 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/Services/GoogleAds/Operations/AdGroup.php: -------------------------------------------------------------------------------- 1 | response()->getId(); 24 | } 25 | 26 | /** 27 | * setId() 28 | * 29 | * @param int $id 30 | * 31 | */ 32 | public function setId($id) 33 | { 34 | $this->request()->setId($id); 35 | 36 | return $this; 37 | } 38 | 39 | /** 40 | * getName() 41 | * 42 | * @return string 43 | * 44 | */ 45 | public function getName() 46 | { 47 | return $this->response()->getName(); 48 | } 49 | 50 | /** 51 | * setName() 52 | * 53 | * @param string $name 54 | * 55 | */ 56 | public function setName($name) 57 | { 58 | $this->request()->setName($name); 59 | 60 | return $this; 61 | } 62 | 63 | /** 64 | * getCampaignId() 65 | * 66 | * @param int $id 67 | * 68 | */ 69 | public function getCampaignId() 70 | { 71 | return $this->response()->getCampaignId(); 72 | } 73 | 74 | /** 75 | * setCampaignId() 76 | * 77 | * @param int $id 78 | * 79 | */ 80 | public function setCampaignId($id) 81 | { 82 | $this->request()->setCampaignId($id); 83 | 84 | return $this; 85 | } 86 | 87 | /** 88 | * getStatus() 89 | * 90 | * @return string 91 | * 92 | */ 93 | public function getStatus() 94 | { 95 | $status = strtoupper($this->response()->getStatus()); 96 | 97 | switch ($status) { 98 | case 'ENABLED': return 'ENABLED'; break; 99 | case 'PAUSED': return 'PAUSED'; break; 100 | case 'REMOVED': return 'DELETED'; break; 101 | default: 102 | } 103 | 104 | return $status; 105 | } 106 | 107 | /** 108 | * setStatus() 109 | * 110 | * @param string $status 111 | * 112 | */ 113 | public function setStatus($status) 114 | { 115 | $status = strtoupper($status); 116 | 117 | if (in_array($status, ['ENABLED','PAUSED'])) { 118 | $this->request()->setStatus($status); 119 | } 120 | 121 | return $this; 122 | } 123 | 124 | /** 125 | * getType() 126 | * 127 | * @reference 128 | * https://github.com/googleads/googleads-php-lib/blob/master/src/Google/AdsApi/AdWords/v201809/cm/AdGroup.php 129 | * 130 | * @return string 131 | * 132 | */ 133 | public function getType() 134 | { 135 | return strtoupper($this->response()->getAdGroupType()); 136 | } 137 | 138 | /** 139 | * getBidStrategy() 140 | * 141 | * @return string 142 | * 143 | */ 144 | public function getBidStrategy() 145 | { 146 | $type = $this->response()->getBiddingStrategyConfiguration()->getBiddingStrategyType() ?? 'UNKNOWN'; 147 | 148 | switch ($type) { 149 | case 'MANUAL_CPC': 150 | 151 | $isEnhanced = false; 152 | 153 | $scheme = $this->response()->getBiddingStrategyConfiguration()->getBiddingScheme() ?? null; 154 | if ($scheme) { 155 | $isEnhanced = $this->response()->getBiddingStrategyConfiguration()->getBiddingScheme()->getEnhancedCpcEnabled() ?? false; 156 | } 157 | 158 | if ($isEnhanced == true) { 159 | return 'ECPC'; 160 | } 161 | 162 | return 'CPC'; 163 | 164 | break; 165 | 166 | case 'TARGET_CPA': 167 | return 'CPA'; 168 | break; 169 | 170 | default: 171 | } 172 | 173 | return $type; 174 | } 175 | 176 | 177 | /** 178 | * getBid() 179 | * 180 | * This will get the bid that is currently active on the bid type 181 | * 182 | */ 183 | public function getBid() 184 | { 185 | $bids = $this->response()->getBiddingStrategyConfiguration()->getBids() ?? []; 186 | 187 | $bidAmount = 0; 188 | 189 | foreach ($bids as $bid) { 190 | if ($bid->getBidsType() == 'CpcBid' && ($this->getBidStrategy() == "CPC" || $this->getBidStrategy() == "ECPC")) { 191 | $bidAmount = $bid->getbid()->getMicroAmount(); 192 | break; 193 | } 194 | 195 | if ($bid->getBidsType() == 'CpaBid' && $this->getBidStrategy() == "CPA") { 196 | $bidAmount = $bid->getbid()->getMicroAmount(); 197 | break; 198 | } 199 | } 200 | 201 | return (($bidAmount) ? round(intval($bidAmount) / 1000000, 2) : 0); 202 | } 203 | 204 | 205 | /** 206 | * setBid() 207 | * 208 | * @reference 209 | * https://github.com/googleads/googleads-php-lib/blob/master/examples/AdWords/v201809/BasicOperations/UpdateAdGroup.php 210 | * 211 | * @param float $amount 212 | * 213 | */ 214 | public function setBid($amount) 215 | { 216 | if ($this->getBidStrategy() == 'CPC' || $this->getBidStrategy() == 'ECPC') { 217 | $bid = new CpcBid(); 218 | 219 | $money = (new Money())->setMicroAmount($amount*1000000); 220 | $bid->setBid($money); 221 | 222 | $biddingStrategyConfiguration = new BiddingStrategyConfiguration(); 223 | $biddingStrategyConfiguration->setBids([$bid]); 224 | 225 | $this->request()->setBiddingStrategyConfiguration($biddingStrategyConfiguration); 226 | } 227 | 228 | return $this; 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /src/Services/GoogleAds/Operations/AdGroupOperations.php: -------------------------------------------------------------------------------- 1 | service = $service; 42 | 43 | $this->request = new AdGroupProxy(); 44 | } 45 | 46 | /** 47 | * request() 48 | * 49 | */ 50 | public function request() 51 | { 52 | return $this->request; 53 | } 54 | 55 | /** 56 | * response() 57 | * 58 | */ 59 | public function response() 60 | { 61 | return $this->response; 62 | } 63 | 64 | /** 65 | * set() 66 | * 67 | */ 68 | public function set(AdGroupProxy $adGroup) 69 | { 70 | $this->response = $adGroup; 71 | 72 | // set up our request if we have not done this yet 73 | $this->request()->setId($adGroup->getId()); 74 | 75 | return $this; 76 | } 77 | 78 | /** 79 | * get() 80 | * 81 | */ 82 | public function get() 83 | { 84 | $this->set($this->sendRequest(false)); 85 | 86 | return $this; 87 | } 88 | 89 | /** 90 | * save() 91 | * 92 | * Post your changes to Google Ads Server 93 | * 94 | */ 95 | public function save() 96 | { 97 | $this->set($this->sendRequest(true)); 98 | 99 | return $this; 100 | } 101 | 102 | /** 103 | * sendRequest() 104 | * 105 | */ 106 | protected function sendRequest($save = false) 107 | { 108 | $adGroup = $this->request(); 109 | 110 | $operation = new AdGroupOperation(); 111 | $operation->setOperand($adGroup); 112 | $operation->setOperator(Operator::SET); 113 | 114 | $serverResponse = ($this->service->call(AdGroupService::class))->mutate([$operation]); 115 | 116 | return ($serverResponse->getValue()[0] ?? null); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/Services/GoogleAds/Operations/Campaign.php: -------------------------------------------------------------------------------- 1 | response()->getId(); 21 | } 22 | 23 | /** 24 | * setId() 25 | * 26 | * @param int $id 27 | * 28 | */ 29 | public function setId($id) 30 | { 31 | $this->request()->setId($id); 32 | 33 | return $this; 34 | } 35 | 36 | /** 37 | * getName() 38 | * 39 | * @return string 40 | * 41 | */ 42 | public function getName() 43 | { 44 | return $this->response()->getName(); 45 | } 46 | 47 | /** 48 | * setName() 49 | * 50 | * @param string $name 51 | * 52 | */ 53 | public function setName($name) 54 | { 55 | $this->request()->setName($name); 56 | 57 | return $this; 58 | } 59 | 60 | /** 61 | * getStatus() 62 | * 63 | * @return string 64 | * 65 | */ 66 | public function getStatus() 67 | { 68 | $status = strtoupper($this->response()->getStatus()); 69 | 70 | switch ($status) { 71 | case 'ENABLED': return 'ENABLED'; break; 72 | case 'PAUSED': return 'PAUSED'; break; 73 | case 'REMOVED': return 'DELETED'; break; 74 | default: 75 | } 76 | 77 | return $status; 78 | } 79 | 80 | /** 81 | * setStatus() 82 | * 83 | * @param string $status 84 | * 85 | */ 86 | public function setStatus($status) 87 | { 88 | $status = strtoupper($status); 89 | 90 | if (in_array($status, ['ENABLED','PAUSED'])) { 91 | $this->request()->setStatus($status); 92 | } 93 | 94 | return $this; 95 | } 96 | 97 | /** 98 | * getBudget() 99 | * 100 | * @return int 101 | * 102 | */ 103 | public function getBudget() 104 | { 105 | $microAmount = $this->response()->getBudget()->getAmount()->getMicroAmount(); 106 | 107 | return (($microAmount) ? round(intval($microAmount) / 1000000, 2) : 0); 108 | } 109 | 110 | /** 111 | * getBudgetDelivery() 112 | * 113 | * @return string 114 | * 115 | */ 116 | public function getBudgetDelivery() 117 | { 118 | return strtoupper($this->response()->getBudget()->getDeliveryMethod() ?? 'UNKNOWN'); 119 | } 120 | 121 | /** 122 | * getChannelType() 123 | * 124 | * @return string 125 | * 126 | */ 127 | public function getChannelType() 128 | { 129 | return strtoupper($this->response()->getAdvertisingChannelType() ?? 'UNKNOWN'); 130 | } 131 | 132 | /** 133 | * getBidStrategy() 134 | * 135 | * @return string 136 | * 137 | */ 138 | public function getBidStrategy() 139 | { 140 | $type = $this->response()->getBiddingStrategyConfiguration()->getBiddingStrategyType() ?? 'UNKNOWN'; 141 | 142 | switch ($type) { 143 | case 'MANUAL_CPC': 144 | 145 | $isEnhanced = false; 146 | 147 | $scheme = $this->response()->getBiddingStrategyConfiguration()->getBiddingScheme() ?? null; 148 | if ($scheme) { 149 | $isEnhanced = $this->response()->getBiddingStrategyConfiguration()->getBiddingScheme()->getEnhancedCpcEnabled() ?? false; 150 | } 151 | 152 | if ($isEnhanced == true) { 153 | return 'ECPC'; 154 | } 155 | 156 | return 'CPC'; 157 | 158 | break; 159 | 160 | case 'TARGET_CPA': 161 | return 'CPA'; 162 | break; 163 | 164 | default: 165 | } 166 | 167 | return $type; 168 | } 169 | 170 | /** 171 | * getTargetCpa() 172 | * 173 | * @return int 174 | * 175 | */ 176 | public function getTargetCpa() 177 | { 178 | if ($this->getBidStrategy() == 'CPA') { 179 | $strategy =@ $this->response()->getBiddingStrategyConfiguration()->getBiddingScheme() ?? null; 180 | 181 | $amount = null; 182 | 183 | if ($strategy) { 184 | $tcpa =@ $strategy->getTargetCpa(); 185 | if ($tcpa) { 186 | $amount =@ ($tcpa->getMicroAmount() ?? 0); 187 | } 188 | } 189 | 190 | return (($amount) ? @round(intval($amount) / 1000000, 2) : 0); 191 | } 192 | 193 | return 0; 194 | } 195 | 196 | 197 | /** 198 | * setTargetCpa() 199 | * 200 | * 201 | */ 202 | public function setTargetCpa($amount = 0) 203 | { 204 | if ($this->getBidStrategy() == 'CPA') { 205 | $money = new Money(); 206 | $money->setMicroAmount($amount*1000000); 207 | 208 | $biddingConfig = $this->response()->getBiddingStrategyConfiguration(); 209 | $biddingScheme = $biddingConfig->getBiddingScheme()->setTargetCpa($money); 210 | 211 | $this->request()->setBiddingStrategyConfiguration($biddingConfig); 212 | } 213 | 214 | return $this; 215 | } 216 | 217 | 218 | /** 219 | * setBudget() 220 | * 221 | * @param int $budget 222 | * 223 | */ 224 | public function setBudget($amount = 0) 225 | { 226 | $money = new Money(); 227 | $money->setMicroAmount($amount*1000000); 228 | 229 | $budget = new Budget(); 230 | $budget->setBudgetId($this->response()->getBudget()->getBudgetId()); 231 | $budget->setAmount($money); 232 | $budget->setDeliveryMethod($this->getBudgetDelivery()); 233 | 234 | $this->request()->setBudget($budget); 235 | 236 | return $this; 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /src/Services/GoogleAds/Operations/CampaignOperations.php: -------------------------------------------------------------------------------- 1 | service = $service; 46 | 47 | $this->request = new CampaignProxy(); 48 | } 49 | 50 | /** 51 | * request() 52 | * 53 | */ 54 | public function request() 55 | { 56 | return $this->request; 57 | } 58 | 59 | /** 60 | * response() 61 | * 62 | */ 63 | public function response() 64 | { 65 | return $this->response; 66 | } 67 | 68 | /** 69 | * set() 70 | * 71 | */ 72 | public function set(CampaignProxy $campaign) 73 | { 74 | $this->response = $campaign; 75 | 76 | // set up our request if we have not done this yet 77 | $this->request()->setId($campaign->getId()); 78 | 79 | return $this; 80 | } 81 | 82 | /** 83 | * get() 84 | * 85 | */ 86 | public function get() 87 | { 88 | $this->set($this->sendRequest(false)); 89 | 90 | return $this; 91 | } 92 | 93 | /** 94 | * save() 95 | * 96 | * Post your changes to Google Ads Server 97 | * 98 | */ 99 | public function save() 100 | { 101 | $this->set($this->sendRequest(true)); 102 | 103 | return $this; 104 | } 105 | 106 | /** 107 | * sendRequest() 108 | * 109 | */ 110 | protected function sendRequest($save = false) 111 | { 112 | $campaign = $this->request(); 113 | 114 | if ($save == true && $campaign->getBudget()) { 115 | $this->saveBudget(); 116 | } 117 | 118 | $operation = new CampaignOperation(); 119 | $operation->setOperand($campaign); 120 | $operation->setOperator(Operator::SET); 121 | 122 | $serverResponse = ($this->service->call(CampaignService::class))->mutate([$operation]); 123 | 124 | return ($serverResponse->getValue()[0] ?? null); 125 | } 126 | 127 | /** 128 | * saveBudget() 129 | * 130 | */ 131 | protected function saveBudget() 132 | { 133 | $campaign = $this->request(); 134 | 135 | $operation = new BudgetOperation(); 136 | $operation->setOperand($campaign->getBudget()); 137 | $operation->setOperator(Operator::SET); 138 | $operations[] = $operation; 139 | 140 | $serverResponse = ($this->service->call(BudgetService::class))->mutate($operations); 141 | 142 | return $serverResponse->getValue()[0] ?? null; 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/Services/GoogleAds/Operations/OfflineConversions.php: -------------------------------------------------------------------------------- 1 | service = $service; 41 | } 42 | 43 | /** 44 | * getConversions() 45 | * 46 | * @return array 47 | */ 48 | public function getConversions() 49 | { 50 | return $this->offlineConversions; 51 | } 52 | 53 | /** 54 | * addBulk() 55 | * Add multiple conversions to the offline array 56 | * 57 | * @param array $conversions 58 | * @return OfflineConversions 59 | */ 60 | public function addBulk(array $conversions = []) 61 | { 62 | foreach ($conversions as $conversion) { 63 | $this->add($conversion); 64 | } 65 | 66 | return $this; 67 | } 68 | 69 | /** 70 | * add() 71 | * Add a single conversion to the offline array 72 | * The conversion array should have keys of 73 | * 74 | * name, time, click_id, value 75 | * 76 | * 77 | * @param array $conversion 78 | * @return OfflineConversions 79 | */ 80 | public function add(array $conversion = []) 81 | { 82 | $gc = new OfflineConversionFeed(); 83 | $gc->setConversionName($conversion['name']); 84 | $gc->setConversionTime($conversion['time']); 85 | $gc->setGoogleClickId($conversion['click_id']); 86 | $gc->setConversionValue($conversion['value']); 87 | 88 | $offlineConversionOperation = new OfflineConversionFeedOperation(); 89 | $offlineConversionOperation->setOperand($gc); 90 | $offlineConversionOperation->setOperator(Operator::ADD); 91 | 92 | $this->offlineConversions[] = $conversion; 93 | $this->mutations[] = $offlineConversionOperation; 94 | 95 | return $this; 96 | } 97 | 98 | /** 99 | * upload() 100 | * 101 | * This method will upload offline converions 102 | * and return the success and errors of each id 103 | * 104 | * https://github.com/googleads/googleads-php-lib/blob/cec475ce83f8cdb923cfc08d9053b48769c0e64a/src/Google/AdsApi/AdWords/v201809/cm/OfflineConversionFeed.php 105 | * 106 | */ 107 | public function upload($outputValue = false) 108 | { 109 | $errorResponse = []; 110 | $successResponse = []; 111 | 112 | foreach ($this->mutations as $i => $mutate) { 113 | $click = $this->offlineConversions[$i] ?? []; 114 | 115 | try { 116 | $result = ($this->service->call(OfflineConversionFeedService::class))->mutate([$mutate]); 117 | $responseValues = $result->getValue(); 118 | foreach ($responseValues as $feed) { 119 | if ($outputValue == true) { 120 | // $successResponse[] = [ 121 | // 'click_id' => $feed->getGoogleClickId(), 122 | // 'value' => $feed->getConversionValue() 123 | // ]; 124 | 125 | $successResponse[] = [ 126 | 'name' => $click['name'], 127 | 'time' => $click['time'], 128 | 'click_id' => $click['click_id'], 129 | 'value' => $click['value'] ?? 0 130 | ]; 131 | } else { 132 | $successResponse[] = $feed->getGoogleClickId(); 133 | } 134 | } 135 | } catch (ApiException $e) { 136 | foreach ($e->getErrors() as $err) { 137 | $reason = $err->getReason(); 138 | $errorResponse[] = [ 139 | 'name' => $click['name'], 140 | 'time' => $click['time'], 141 | 'click_id' => $click['click_id'], 142 | 'value' => $click['value'] ?? 0, 143 | 'error' => $reason 144 | ]; 145 | } 146 | } 147 | } 148 | 149 | // prevent abuse in api requests 150 | // default is 0.05 seconds per request 151 | usleep(050000); 152 | 153 | return [ 154 | 'errors' => ($errorResponse) ? $errorResponse : false, 155 | 'success' => ($successResponse) ? $successResponse : false 156 | ]; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/Services/GoogleAds/ReportDownload.php: -------------------------------------------------------------------------------- 1 | service = $service; 31 | 32 | $reportDownloader = new ReportDownloader($this->service->session()); 33 | 34 | $reportSettingsOverride = (new ReportSettingsBuilder()) 35 | ->includeZeroImpressions(false) 36 | ->skipReportHeader(true) 37 | ->skipReportSummary(true) 38 | ->build(); 39 | 40 | $reportDownloadResult = $reportDownloader->downloadReport($reportDefinition, $reportSettingsOverride); 41 | 42 | $this->results = $reportDownloadResult->getAsString(); 43 | } 44 | 45 | /** 46 | * getResults() 47 | * 48 | * 49 | * @return string results 50 | */ 51 | public function getResults() 52 | { 53 | return $this->results ?? false; 54 | } 55 | 56 | /** 57 | * aggregate() 58 | * 59 | * 60 | * @return collection results 61 | */ 62 | public function aggregate($field) 63 | { 64 | $results = $this->toArray(); 65 | 66 | $only = [ 67 | 'impressions','clicks','cost','conversions','conversion_value' 68 | ]; 69 | 70 | $r = []; 71 | foreach ($results as $key=>$value) { 72 | if (isset($r[$value[$field]])) { 73 | $x = $r[$value[$field]]; 74 | 75 | foreach ($value as $k=>$v) { 76 | if (!in_array($k, $only)) { 77 | continue; 78 | } 79 | 80 | $n = $x[$k]; 81 | if (!is_numeric($n)) { 82 | continue 2; 83 | } 84 | if (!is_numeric($v)) { 85 | continue 2; 86 | } 87 | 88 | $value[$k] = $v+$n; 89 | } 90 | 91 | $r[$value[$field]] = $value; 92 | } else { 93 | $r[$value[$field]] = $value; 94 | } 95 | } 96 | 97 | return collect($r); 98 | } 99 | 100 | /** 101 | * toArray() 102 | * 103 | * 104 | * @return array results 105 | */ 106 | public function toArray() 107 | { 108 | if (is_array($this->results)) { 109 | return $this->results; 110 | } 111 | 112 | // get all rows 113 | $rows = array_map('str_getcsv', explode("\n", $this->results)); 114 | // get the header row 115 | $headers = $rows[0]; 116 | // remove first row (headers) 117 | array_shift($rows); 118 | 119 | $header = []; 120 | foreach ($headers as $label) { 121 | $label = strtolower($label); 122 | 123 | switch ($label) { 124 | case 'day': $label = 'date'; break; 125 | case 'campaign id': $label = 'campaign_id'; break; 126 | case 'campaign state': $label = 'campaign_status'; break; 127 | case 'campaign': $label = 'campaign_name'; break; 128 | case 'ad group': $label = 'ad_group_name'; break; 129 | case 'advertising channel': $label = 'channel'; break; 130 | case 'ad group id': $label = 'ad_group_id'; break; 131 | case 'total conv. value': $label = 'conversion_value'; break; 132 | case 'avg. position': $label = 'avg_position'; break; 133 | 134 | case 'hour of day': $label = 'hour'; break; 135 | case 'day of week': $label = 'day'; break; 136 | 137 | // criteria 138 | case 'age range': $label = 'age_range'; break; 139 | case 'searchterm': $label = 'search_term'; break; 140 | case 'most specific location': $label = 'location'; break; 141 | default: 142 | } 143 | 144 | $header[] = str_replace([' ','/'], '_', $label); 145 | } 146 | 147 | $report = []; 148 | foreach ($rows as $index=>$columns) { 149 | $r = []; 150 | foreach ($columns as $index2=>$cs) { 151 | if (!isset($header[$index2])) { 152 | continue; 153 | } 154 | 155 | $n = $header[$index2]; 156 | 157 | if ($n == 'cost') { 158 | $cs = round(intval($cs) / 1000000, 2); 159 | } 160 | 161 | $r[$n] = $cs; 162 | } 163 | 164 | // add in columns (headers) that are missing from the response 165 | foreach ($header as $h) { 166 | if (!isset($r[$h])) { 167 | $r[$h] = 0; 168 | } 169 | } 170 | 171 | $report[] = $r; 172 | } 173 | 174 | ksort($report); 175 | 176 | return $report; 177 | } 178 | 179 | /** 180 | * toCollection() 181 | * 182 | * 183 | * @return \Illuminate\Support\Collection results 184 | */ 185 | public function toCollection() 186 | { 187 | return collect($this->toArray()); 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/Services/GoogleAds/Reports.php: -------------------------------------------------------------------------------- 1 | service = $service; 52 | } 53 | 54 | /** 55 | * setDateRange() 56 | * 57 | * 58 | * @return self 59 | */ 60 | public function setDateRange($dateFrom, $dateTo) 61 | { 62 | $this->dateRange[] = $dateFrom; 63 | $this->dateRange[] = $dateTo; 64 | 65 | return $this; 66 | } 67 | 68 | /** 69 | * setFields() 70 | * 71 | * 72 | * @return self 73 | */ 74 | public function setFields($fields, $auto = false) 75 | { 76 | if ($auto == false) { 77 | $this->fields = $fields; 78 | } 79 | 80 | if ($auto == true && empty($this->fields)) { 81 | $this->fields = $fields; 82 | } 83 | 84 | return $this; 85 | } 86 | 87 | /** 88 | * setFilters() 89 | * 90 | * 91 | * @return self 92 | */ 93 | public function setFilters($filters) 94 | { 95 | $this->filters = $filters; 96 | 97 | return $this; 98 | } 99 | 100 | /** 101 | * selectors() 102 | * 103 | * 104 | */ 105 | protected function getSelector($dateRange, $fields = [], $filters = []) 106 | { 107 | $selector = new Selector(); 108 | $selector->setFields($fields); 109 | $selector->setDateRange([ 110 | 'min' => str_replace('-', '', $dateRange[0]), 111 | 'max' => str_replace('-', '', $dateRange[1]) 112 | ]); 113 | 114 | if ($filters) { 115 | $predicates = []; 116 | 117 | foreach ($filters as $filter) { 118 | $predicates[] = new Predicate($filter['field'], PredicateOperator::IN, $filter['values']); 119 | } 120 | 121 | $selector->setPredicates($predicates); 122 | } 123 | 124 | return $selector; 125 | } 126 | 127 | /** 128 | * reportDefinition() 129 | * 130 | * 131 | */ 132 | protected function reportDefinition($reportType) 133 | { 134 | $reportDefinition = new ReportDefinition(); 135 | $reportDefinition->setSelector($this->getSelector($this->dateRange, $this->fields, $this->filters)); 136 | $reportDefinition->setReportName('Performance report #' . uniqid()); 137 | $reportDefinition->setDateRangeType(ReportDefinitionDateRangeType::CUSTOM_DATE); 138 | $reportDefinition->setReportType($reportType); 139 | $reportDefinition->setDownloadFormat(DownloadFormat::CSV); 140 | 141 | return $reportDefinition; 142 | } 143 | 144 | 145 | /** 146 | * reportDownload() 147 | * 148 | * 149 | */ 150 | public function reportDownload($reportType) 151 | { 152 | $reportDefinition = $this->reportDefinition($reportType); 153 | 154 | return (new ReportDownload($this->service, $reportDefinition)); 155 | } 156 | 157 | 158 | /** 159 | * getAccountReport() 160 | * https://developers.google.com/adwords/api/docs/appendix/reports/account-performance-report 161 | * 162 | * 163 | */ 164 | public function getAccountReport($aggregation = 'Date') 165 | { 166 | $this->setFields([ 167 | $aggregation, 168 | 'Impressions', 169 | 'Clicks', 170 | 'Cost', 171 | 'Conversions', 172 | 'ConversionValue' 173 | ], true); 174 | 175 | return $this->reportDownload(ReportDefinitionReportType::ACCOUNT_PERFORMANCE_REPORT)->toCollection(); 176 | } 177 | 178 | 179 | /** 180 | * getCampaignReport() 181 | * https://developers.google.com/adwords/api/docs/appendix/reports/campaign-performance-report 182 | * 183 | * 184 | */ 185 | public function getCampaignReport() 186 | { 187 | $this->setFields([ 188 | 'Date', 189 | 'AdvertisingChannelType', 190 | 'CampaignStatus', 191 | 'CampaignName', 192 | 'CampaignId', 193 | 'Impressions', 194 | 'Clicks', 195 | 'Cost', 196 | 'Conversions', 197 | 'ConversionValue' 198 | ], true); 199 | 200 | return $this->reportDownload(ReportDefinitionReportType::CAMPAIGN_PERFORMANCE_REPORT)->toCollection(); 201 | } 202 | 203 | 204 | /** 205 | * getAdGroupReport() 206 | * https://developers.google.com/adwords/api/docs/appendix/reports/adgroup-performance-report 207 | * 208 | * 209 | */ 210 | public function getAdGroupReport() 211 | { 212 | $this->setFields([ 213 | 'Date', 214 | 'AdGroupId', 215 | 'AdGroupName', 216 | 'CampaignId', 217 | 'CampaignName', 218 | 'Impressions', 219 | 'Clicks', 220 | 'Cost', 221 | 'Conversions', 222 | 'ConversionValue', 223 | 'AveragePosition' 224 | ], true); 225 | 226 | return $this->reportDownload(ReportDefinitionReportType::ADGROUP_PERFORMANCE_REPORT)->toCollection(); 227 | } 228 | 229 | /** 230 | * getFinalUrlReport() 231 | * 232 | */ 233 | public function getFinalUrlReport() 234 | { 235 | $this->setFields([ 236 | 'Date', 237 | 'CampaignId', 238 | 'CampaignName', 239 | 'Impressions', 240 | 'Clicks', 241 | 'Cost', 242 | 'Conversions', 243 | 'ConversionValue', 244 | 'EffectiveFinalUrl' 245 | ], true); 246 | 247 | return $this->reportDownload(ReportDefinitionReportType::FINAL_URL_REPORT)->toCollection(); 248 | } 249 | 250 | 251 | /** 252 | * getAgeRangeReport() 253 | * https://developers.google.com/adwords/api/docs/appendix/reports/age-range-performance-report 254 | * 255 | * 256 | */ 257 | public function getAgeRangeReport() 258 | { 259 | return $this->getCriteriaReport(ReportDefinitionReportType::AGE_RANGE_PERFORMANCE_REPORT, 'age_range', 'Criteria'); 260 | } 261 | 262 | /** 263 | * getGenderReport() 264 | * 265 | */ 266 | public function getGenderReport() 267 | { 268 | return $this->getCriteriaReport(ReportDefinitionReportType::GENDER_PERFORMANCE_REPORT, 'gender', 'Criteria'); 269 | } 270 | 271 | /** 272 | * getPlacementReport() 273 | * 274 | */ 275 | public function getPlacementReport() 276 | { 277 | return $this->getCriteriaReport(ReportDefinitionReportType::PLACEMENT_PERFORMANCE_REPORT, 'placement', 'Criteria'); 278 | } 279 | 280 | /** 281 | * getPlacementUrlReport() 282 | * 283 | */ 284 | public function getPlacementUrlReport() 285 | { 286 | return $this->getCriteriaReport(ReportDefinitionReportType::URL_PERFORMANCE_REPORT, 'url', 'Url'); 287 | } 288 | 289 | /** 290 | * getSearchTermReport() 291 | * 292 | */ 293 | public function getSearchTermReport() 294 | { 295 | return $this->getCriteriaReport(ReportDefinitionReportType::SEARCH_QUERY_PERFORMANCE_REPORT, 'search_term', 'Query'); 296 | } 297 | 298 | 299 | /** 300 | * getCriteriaReport() 301 | * 302 | */ 303 | public function getCriteriaReport($report, $field, $setField) 304 | { 305 | $this->setFields([ 306 | $setField, 307 | 'Impressions', 308 | 'Clicks', 309 | 'Cost', 310 | 'Conversions', 311 | 'ConversionValue' 312 | ], true); 313 | 314 | return $this->reportDownload($report)->aggregate($field); 315 | } 316 | } 317 | -------------------------------------------------------------------------------- /src/Services/GoogleAds/Service.php: -------------------------------------------------------------------------------- 1 | clientId = $clientId; 56 | return $this; 57 | } 58 | 59 | /** 60 | * getClientId() 61 | * 62 | * @return string 63 | */ 64 | public function getClientId() { 65 | return $this->clientId; 66 | } 67 | 68 | /** 69 | * fetch() 70 | * 71 | * 72 | */ 73 | public function fetch() { 74 | return (new Fetch($this)); 75 | } 76 | 77 | /** 78 | * reports() 79 | * 80 | * 81 | */ 82 | public function reports($dateFrom, $dateTo) { 83 | return (new Reports($this))->setDateRange($dateFrom, $dateTo); 84 | } 85 | 86 | /** 87 | * call() 88 | * 89 | * 90 | */ 91 | public function call($service) { 92 | return (new AdWordsServices())->get($this->session(), $service); 93 | } 94 | 95 | /** 96 | * offlineConversionImport() 97 | * 98 | * 99 | */ 100 | public function offlineConversionImport(array $conversions = []) { 101 | return (new OfflineConversions($this))->addBulk($conversions); 102 | } 103 | 104 | /** 105 | * adGroup() 106 | * 107 | * @reference 108 | * https://github.com/googleads/googleads-php-lib/blob/master/examples/AdWords/v201809/BasicOperations/UpdateAdGroup.php 109 | * 110 | * @return AdGroup 111 | */ 112 | public function adGroup($adGroup) 113 | { 114 | if ($adGroup instanceof AdGroupProxy) { 115 | return (new AdGroup($this))->set($adGroup); 116 | } else { 117 | return (new AdGroup($this))->setId($adGroup)->get(); 118 | } 119 | } 120 | 121 | /** 122 | * campaign() 123 | * 124 | * @return Campaign 125 | */ 126 | public function campaign($campaign) 127 | { 128 | if ($campaign instanceof CampaignProxy) { 129 | return (new Campaign($this))->set($campaign); 130 | } else { 131 | return (new Campaign($this))->setId($campaign)->get(); 132 | } 133 | } 134 | 135 | /** 136 | * session() 137 | * 138 | * Retrieves an instance of a session or creates a new one 139 | * 140 | * @return object $this->session 141 | */ 142 | public function session() 143 | { 144 | if (!$this->session) { 145 | $this->session = (new AdWordsSessionBuilder()) 146 | ->from($this->configuration()) 147 | ->withOAuth2Credential($this->oAuth2credentials()) 148 | ->withClientCustomerId($this->getClientId()) 149 | ->build(); 150 | } 151 | 152 | return $this->session; 153 | } 154 | 155 | 156 | /** 157 | * oAuth2credentials() 158 | * 159 | */ 160 | protected function oAuth2credentials($env = null) 161 | { 162 | return (new OAuth2TokenBuilder()) 163 | ->from($this->configuration()) 164 | ->build(); 165 | } 166 | 167 | /** 168 | * Configuration 169 | * 170 | * @return Configuration 171 | */ 172 | public function configuration($config = []) 173 | { 174 | if (!$config) { 175 | // use laravel config 176 | $config = config('google-ads'); 177 | 178 | // check if config already exist 179 | if ($this->config) { 180 | return $this->config; 181 | } 182 | } 183 | 184 | // create a new config 185 | return ($this->config = (new Configuration($config))); 186 | } 187 | } 188 | --------------------------------------------------------------------------------