├── .conform.yaml ├── .php-cs-fixer.php ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── art └── logo.png ├── composer.json ├── config └── google-ads.php ├── phpstan-baseline.neon ├── phpstan.neon ├── pint.json └── src ├── Facades └── GoogleAds.php ├── LaravelGoogleAds.php ├── LaravelGoogleAdsServiceProvider.php └── Models └── LaravelGoogleAdsModel.php /.conform.yaml: -------------------------------------------------------------------------------- 1 | policies: 2 | - type: commit 3 | spec: 4 | header: 5 | length: 75 6 | imperative: true 7 | case: lower 8 | invalidLastCharacters: ".?:!$" 9 | body: 10 | required: false 11 | dco: false 12 | gpg: false 13 | spellcheck: 14 | locale: US 15 | maximumOfOneCommit: false 16 | conventional: 17 | types: 18 | - ci 19 | - chore 20 | - feat 21 | - fix 22 | - test 23 | scopes: 24 | - ".*" 25 | -------------------------------------------------------------------------------- /.php-cs-fixer.php: -------------------------------------------------------------------------------- 1 | in([ 5 | __DIR__ . '/src', 6 | __DIR__ . '/tests', 7 | ]) 8 | ->name('*.php') 9 | ->notName('*.blade.php') 10 | ->ignoreDotFiles(true) 11 | ->ignoreVCS(true); 12 | 13 | return (new PhpCsFixer\Config()) 14 | ->setRules([ 15 | '@PSR12' => true, 16 | 'array_syntax' => ['syntax' => 'short'], 17 | 'ordered_imports' => ['sort_algorithm' => 'alpha'], 18 | 'no_unused_imports' => true, 19 | 'not_operator_with_successor_space' => true, 20 | 'trailing_comma_in_multiline' => true, 21 | 'phpdoc_scalar' => true, 22 | 'unary_operator_spaces' => true, 23 | 'binary_operator_spaces' => true, 24 | 'blank_line_before_statement' => [ 25 | 'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'], 26 | ], 27 | 'phpdoc_single_line_var_spacing' => true, 28 | 'phpdoc_var_without_name' => true, 29 | 'class_attributes_separation' => [ 30 | 'elements' => [ 31 | 'method' => 'one', 32 | ], 33 | ], 34 | 'method_argument_space' => [ 35 | 'on_multiline' => 'ensure_fully_multiline', 36 | 'keep_multiple_spaces_after_comma' => true, 37 | ], 38 | 'single_trait_insert_per_statement' => true, 39 | ]) 40 | ->setFinder($finder); 41 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `laravel-google-ads` will be documented in this file. 4 | 5 | ## 1.0.1 - 2023-03-27 6 | 7 | - Add support Laravel 8.x 8 | 9 | ## 1.0.0 - 2023-03-27 10 | 11 | - Initial release 12 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) lucasgiovanny 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Laravel Google Ads RES 2 | 3 | Laravel Google Ads Rest is a package created by [Lucas Giovanny](https://github.com/lucasgiovanny) that provides a convenient and user-friendly way to utilize the Google Ads API through a REST protocol. With this package, users can easily integrate their Laravel application with the Google Ads API, without the need for extensive coding or technical expertise. This package simplifies the process of accessing and manipulating data from Google Ads campaigns, making it easier for developers to build powerful applications that leverage the full capabilities of the Google Ads API. 4 | 5 | ![GitHub release (latest by date)](https://img.shields.io/github/v/release/lucasgiovanny/laravel-google-ads-rest?label=last%20version) 6 | ![GitHub](https://img.shields.io/github/license/lucasgiovanny/laravel-google-ads-rest) 7 | 8 | ## Documentation 9 | 10 | ### Installation 11 | 12 | To install the package, you can use Composer by running the following command: 13 | 14 | ```bash 15 | composer require lucasgiovanny/laravel-google-ads-rest 16 | ``` 17 | 18 | ### Configuration 19 | 20 | To use the package, you must configure your credentials on your `.env` file. 21 | 22 | ```bash 23 | GOOGLEADS_CLIENT_ID= 24 | GOOGLEADS_CLIENT_SECRET= 25 | GOOGLEADS_DEVELOPER_TOKEN= 26 | GOOGLEADS_REFRESH_TOKEN= 27 | GOOGLEADS_DEFAULT_ACCOUNT= 28 | ``` 29 | 30 | ### Usage 31 | 32 | You can use the package by calling the `GoogleAds` facade. 33 | 34 | Example: 35 | 36 | ```php 37 | use LucasGiovanny\LaravelGoogleAds\Facades\GoogleAds; 38 | 39 | GoogleAds::account('ACCOUNT_ID') 40 | ->from('ad_group_ad') 41 | ->select(['metrics.cost_micros', 'segments.date']) 42 | ->where('segments.date', 'BETWEEN', '2023-01-01 AND 2023-01-31') 43 | ->get() 44 | ``` 45 | 46 | #### Method: `account($accountId)` 47 | 48 | The `account()` method sets the Google Ads account ID to be used for the query. It takes in a string parameter `$accountId` which is the Google Ads account ID. 49 | 50 | #### Method: `from($resource)` 51 | 52 | The `from()` method specifies the **resource** to be queried. It takes in a string parameter `$resource` which is the name of the resource in the Google Ads API. 53 | 54 | #### Method: `select($fields)` 55 | 56 | The `select()` method specifies the **fields** to be retrieved from the specified resource. It takes in an array parameter `$fields` which is a list of the field names as strings. 57 | 58 | #### Method: `where($field, $operator, $value)` 59 | 60 | The `where()` method specifies a filter to apply to the query. It takes in three parameters: `$field` which is the name of the field to filter on, `$operator` which is the filter operator (e.g. BETWEEN, EQUALS, etc.), and `$value` which is the value to filter on. 61 | 62 | #### Method: `get()` 63 | 64 | The `get()` method executes the query and returns the result as an array of objects. 65 | 66 | ## Changelog 67 | 68 | Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. 69 | 70 | ## Contributing 71 | 72 | Please see [CONTRIBUTING](.github/CONTRIBUTING.md) for details. 73 | 74 | ## Security Vulnerabilities 75 | 76 | Please review [our security policy](../../security/policy) on how to report security vulnerabilities. 77 | 78 | ## Credits 79 | 80 | - [Lucas Giovanny](https://github.com/lucasgiovanny) 81 | - [All Contributors](../../contributors) 82 | 83 | ## License 84 | 85 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 86 | -------------------------------------------------------------------------------- /art/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucasgiovanny/laravel-google-ads-rest/90f7151ff4c8b26ad857e87b2340595b660e5dd5/art/logo.png -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lucasgiovanny/laravel-google-ads-rest", 3 | "description": "Use Laravel Google Ads REST API easy", 4 | "keywords": [ 5 | "lucasgiovanny", 6 | "laravel", 7 | "laravel-google-ads", 8 | "google-ads" 9 | ], 10 | "homepage": "https://github.com/lucasgiovanny/laravel-google-ads-rest", 11 | "license": "MIT", 12 | "authors": [ 13 | { 14 | "name": "Lucas Giovanny", 15 | "email": "lucasgiovanny@gmail.com", 16 | "role": "Developer" 17 | } 18 | ], 19 | "require": { 20 | "php": "^8.0", 21 | "guzzlehttp/guzzle": "^7.3", 22 | "illuminate/contracts": "^8.0|^9.0|^10.0|^11.0", 23 | "spatie/laravel-package-tools": "^1.14.0" 24 | }, 25 | "require-dev": { 26 | "laravel/pint": "^1.0", 27 | "nunomaduro/collision": "^8.1", 28 | "nunomaduro/larastan": "^2.0.1", 29 | "orchestra/testbench": "^9.1", 30 | "pestphp/pest": "^2.2", 31 | "pestphp/pest-plugin-arch": "^2.0", 32 | "pestphp/pest-plugin-laravel": "^2.0", 33 | "phpstan/extension-installer": "^1.1", 34 | "phpstan/phpstan-deprecation-rules": "^1.0", 35 | "phpstan/phpstan-phpunit": "^1.0", 36 | "spatie/laravel-ray": "^1.26" 37 | }, 38 | "autoload": { 39 | "psr-4": { 40 | "LucasGiovanny\\LaravelGoogleAds\\": "src" 41 | } 42 | }, 43 | "autoload-dev": { 44 | "psr-4": { 45 | "LucasGiovanny\\LaravelGoogleAds\\Tests\\": "tests" 46 | } 47 | }, 48 | "scripts": { 49 | "post-autoload-dump": "@php ./vendor/bin/testbench package:discover --ansi", 50 | "analyse": "vendor/bin/phpstan analyse", 51 | "test": "vendor/bin/pest", 52 | "test-coverage": "vendor/bin/pest --coverage", 53 | "format": "vendor/bin/pint" 54 | }, 55 | "config": { 56 | "sort-packages": true, 57 | "allow-plugins": { 58 | "pestphp/pest-plugin": true, 59 | "phpstan/extension-installer": true 60 | } 61 | }, 62 | "extra": { 63 | "laravel": { 64 | "providers": [ 65 | "LucasGiovanny\\LaravelGoogleAds\\LaravelGoogleAdsServiceProvider" 66 | ] 67 | } 68 | }, 69 | "minimum-stability": "dev", 70 | "prefer-stable": true 71 | } 72 | -------------------------------------------------------------------------------- /config/google-ads.php: -------------------------------------------------------------------------------- 1 | env('GOOGLEADS_CLIENT_ID'), 15 | 16 | 'client-secret' => env('GOOGLEADS_CLIENT_SECRET'), 17 | 18 | 'developer-token' => env('GOOGLEADS_DEVELOPER_TOKEN'), 19 | 20 | 'refresh-token' => env('GOOGLEADS_REFRESH_TOKEN'), 21 | 22 | 'default-account' => env('GOOGLEADS_DEFAULT_ACCOUNT'), 23 | 24 | ]; 25 | -------------------------------------------------------------------------------- /phpstan-baseline.neon: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucasgiovanny/laravel-google-ads-rest/90f7151ff4c8b26ad857e87b2340595b660e5dd5/phpstan-baseline.neon -------------------------------------------------------------------------------- /phpstan.neon: -------------------------------------------------------------------------------- 1 | includes: 2 | - phpstan-baseline.neon 3 | 4 | parameters: 5 | level: 5 6 | paths: 7 | - src/ 8 | tmpDir: build/phpstan 9 | checkOctaneCompatibility: true 10 | checkModelProperties: true 11 | checkMissingIterableValueType: false 12 | -------------------------------------------------------------------------------- /pint.json: -------------------------------------------------------------------------------- 1 | { 2 | "preset": "laravel", 3 | "rules": { 4 | "blank_line_before_statement": true, 5 | "concat_space": { 6 | "spacing": "one" 7 | }, 8 | "method_argument_space": true, 9 | "single_trait_insert_per_statement": true, 10 | "types_spaces": { 11 | "space": "single" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Facades/GoogleAds.php: -------------------------------------------------------------------------------- 1 | ', 36 | '>=', 37 | '<', 38 | '<=', 39 | 'IN', 40 | 'NOT IN', 41 | 'LIKE', 42 | 'NOT LIKE', 43 | 'CONTAINS ANY', 44 | 'CONTAINS ALL', 45 | 'CONTAINS NONE', 46 | 'IS NULL', 47 | 'IS NOT NULL', 48 | 'DURING', 49 | 'BETWEEN', 50 | 'REGEXP_MATCH', 51 | 'NOT REGEXP_MATCH', 52 | ]; 53 | 54 | public const FUNCTIONS = [ 55 | 'LAST_14_DAYS', 56 | 'LAST_30_DAYS', 57 | 'LAST_7_DAYS', 58 | 'LAST_BUSINESS_WEEK', 59 | 'LAST_MONTH', 60 | 'LAST_WEEK_MON_SUN', 61 | 'LAST_WEEK_SUN_SAT', 62 | 'THIS_MONTH', 63 | 'THIS_WEEK_MON_TODAY', 64 | 'THIS_WEEK_SUN_TODAY', 65 | 'TODAY', 66 | 'YESTERDAY', 67 | ]; 68 | 69 | /** 70 | * Headers for request 71 | */ 72 | protected array $headers = [ 73 | 'Content-Type' => 'application/json', 74 | ]; 75 | 76 | /** 77 | * Construct the class with dependencies 78 | * 79 | * @param HttpClient $http 80 | * @return void 81 | */ 82 | public function __construct() 83 | { 84 | $this->http = new HttpClient; 85 | } 86 | 87 | /** 88 | * Set the resource to be used 89 | * 90 | * 91 | * @return $this 92 | */ 93 | public function select(string|array $fields): self 94 | { 95 | $this->fields = is_array($fields) ? $fields : [$fields]; 96 | 97 | return $this; 98 | } 99 | 100 | /** 101 | * Set the resource to be used 102 | * 103 | * 104 | * @return $this 105 | */ 106 | public function account(string $account): self 107 | { 108 | $this->account = \Illuminate\Support\Str::remove('-', $account); 109 | 110 | return $this; 111 | } 112 | 113 | /** 114 | * Set the resource to be used 115 | * 116 | * 117 | * @return $this 118 | */ 119 | public function from(string $resource): self 120 | { 121 | $this->resource = $resource; 122 | 123 | return $this; 124 | } 125 | 126 | /** 127 | * Add a filter to the web service call 128 | * 129 | * @param mixed $value 130 | * @return $this 131 | * 132 | * @throws Exception 133 | */ 134 | public function where(string $field, string $operatorOrValue, mixed $value = null): self 135 | { 136 | $operator = $value ? $operatorOrValue : '='; 137 | 138 | if (! in_array(strtoupper($operator), self::WHERE_OPERATORS)) { 139 | throw new Exception('Invalid filter operator'); 140 | } 141 | 142 | $this->wheres[] = [ 143 | 'field' => $field, 144 | 'operator' => strtoupper($operator), 145 | 'value' => $value ?: $operatorOrValue, 146 | ]; 147 | 148 | return $this; 149 | } 150 | 151 | /** 152 | * Execute the get request 153 | * 154 | * @return \Illuminate\Support\Collection 155 | * 156 | * @throws Exception 157 | */ 158 | public function get() 159 | { 160 | if (! $this->resource || ! $this->fields || ! $this->account) { 161 | throw new Exception('Invalid call'); 162 | } 163 | 164 | return $this->call(); 165 | } 166 | 167 | protected function query(): string 168 | { 169 | $query = 'SELECT '; 170 | $query .= implode(',', $this->fields); 171 | $query .= ' FROM '.$this->resource; 172 | 173 | if ($this->wheres) { 174 | $query .= ' WHERE '; 175 | foreach ($this->wheres as $where) { 176 | $query .= $where['field'].' '.$where['operator'].' '.$where['value']; 177 | $query .= end($this->wheres) === $where ? '' : ' AND '; 178 | } 179 | } 180 | 181 | return $query; 182 | } 183 | 184 | /** 185 | * @return string|null 186 | * 187 | * @throws \GuzzleHttp\Exception\GuzzleException 188 | */ 189 | protected function token() 190 | { 191 | $url = 'https://www.googleapis.com/oauth2/v3/token'; 192 | 193 | $res = $this->http->request('POST', $url, [ 194 | RequestOptions::FORM_PARAMS => [ 195 | 'grant_type' => 'refresh_token', 196 | 'client_secret' => config('google-ads.client-secret'), 197 | 'client_id' => config('google-ads.client-id'), 198 | 'refresh_token' => config('google-ads.refresh-token'), 199 | ], 200 | ]); 201 | 202 | $json = $res->getBody() ? json_decode($res->getBody(), true) : null; 203 | 204 | return $json['access_token'] ?? null; 205 | } 206 | 207 | /** 208 | * Internal method to make the correct request call 209 | * 210 | * @return array 211 | */ 212 | protected function call() 213 | { 214 | $url = trim($this->apiURL, '/').'/'.trim($this->apiVersion, '/').'/customers/'.trim($this->account, '/').'/googleAds:searchStream'; 215 | 216 | $res = $this->http->request( 217 | 'POST', 218 | $url, 219 | [ 220 | RequestOptions::HEADERS => array_merge( 221 | $this->headers, 222 | [ 223 | 'developer-token' => config('google-ads.developer-token'), 224 | 'login-customer-id' => config('google-ads.default-account'), 225 | 'Authorization' => 'Bearer '.$this->token(), 226 | ] 227 | ), 228 | RequestOptions::BODY => json_encode(['query' => $this->query()]), 229 | ] 230 | ); 231 | 232 | $results = $res->getBody() ? json_decode($res->getBody(), true)[0]['results'] ?? [] : null; 233 | 234 | foreach ($results as $result) { 235 | $return[] = new LaravelGoogleAdsModel($this->resource, array_merge($result[$this->resource] ?? $result, ['metrics' => $result['metrics'] ?? null], ['segments' => $result['segments'] ?? null])); 236 | } 237 | 238 | return collect($return ?? []); 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /src/LaravelGoogleAdsServiceProvider.php: -------------------------------------------------------------------------------- 1 | name('laravel-google-ads') 19 | ->hasConfigFile(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Models/LaravelGoogleAdsModel.php: -------------------------------------------------------------------------------- 1 | attributes[$name] ?? null; 26 | } 27 | } 28 | --------------------------------------------------------------------------------