revolution / laravel-google-searchconsole
Google SearchConsole API for Laravel
Fund package maintenance!
invokable
Installs: 3 655
Dependents: 0
Suggesters: 0
Security: 0
Stars: 14
Watchers: 3
Forks: 0
Open Issues: 0
Requires
- php: ^8.2
- illuminate/support: ^11.0||^12.0
- revolution/laravel-google-sheets: ^7.0
Requires (Dev)
- laravel/pint: ^1.22
- orchestra/testbench: ^10.0
README
https://developers.google.com/webmaster-tools
Overview
This Laravel package provides a comprehensive PHP wrapper for the Google Search Console API, enabling you to seamlessly integrate Search Console functionality into your Laravel applications. With this package, you can:
- Query Search Analytics Data: Retrieve search performance metrics including impressions, clicks, CTR, and average position
- Analyze Website Performance: Get detailed insights about your website's performance in Google Search results
- Filter by Dimensions: Query data by page, query, country, device, and more
- Manage Site Properties: List and manage your Search Console properties
- Flexible Authentication: Support for both OAuth 2.0 (user-based) and Service Account (server-to-server) authentication
- Laravel Integration: Built specifically for Laravel with facades, service providers, and Artisan commands
The package leverages the powerful revolution/laravel-google-sheets
dependency for Google API client management and authentication, providing automatic token refresh and robust error handling.
Perfect for: SEO tools, analytics dashboards, automated reporting, website monitoring, and any application that needs to access Google Search Console data programmatically.
Requirements
- PHP >= 8.2
- Laravel >= 11.0
Installation
composer require revolution/laravel-google-searchconsole
php artisan vendor:publish --tag="google-config"
Uninstall
composer remove revolution/laravel-google-searchconsole
Sample project
https://github.com/invokable/search-console-project
Configuration
Authentication Methods
This package supports two authentication methods for accessing the Google Search Console API:
- OAuth 2.0: Recommended for user-based access when you need to access data on behalf of individual Google users
- Service Account: Ideal for server-to-server applications with automated access to specific properties
- Note: API Key authentication is NOT supported by Google Search Console API
Prerequisites
- Go to the Google Cloud Console
- Create a new project or select an existing one
- Enable the Google Search Console API
- Create credentials based on your chosen authentication method
OAuth 2.0 Setup (using Socialite)
For OAuth authentication, this package works seamlessly with Laravel Socialite's official Google driver.
1. Install Laravel Socialite
composer require laravel/socialite
2. Create OAuth 2.0 Credentials
In Google Cloud Console:
- Go to "Credentials" → "Create Credentials" → "OAuth 2.0 Client IDs"
- Set application type to "Web application"
- Add your authorized redirect URIs (e.g.,
https://yourapp.com/auth/google/callback
)
3. Configure OAuth Settings
Add to config/services.php
:
'google' => [ 'client_id' => env('GOOGLE_CLIENT_ID'), 'client_secret' => env('GOOGLE_CLIENT_SECRET'), 'redirect' => env('GOOGLE_REDIRECT_URI'), ],
Add to config/google.php
:
'client_id' => env('GOOGLE_CLIENT_ID'), 'client_secret' => env('GOOGLE_CLIENT_SECRET'), 'redirect_uri' => env('GOOGLE_REDIRECT_URI'), 'scopes' => [\Google\Service\Webmasters::WEBMASTERS], 'access_type' => 'offline', 'prompt' => 'consent select_account',
4. Environment Variables for OAuth
GOOGLE_CLIENT_ID=your_oauth_client_id GOOGLE_CLIENT_SECRET=your_oauth_client_secret GOOGLE_REDIRECT_URI=https://yourapp.com/auth/google/callback
Service Account Setup
Service Accounts provide server-to-server authentication without user interaction. The Service Account email must be added as an owner or user in Search Console for each property you want to access.
1. Create Service Account Credentials
In Google Cloud Console:
- Go to "Credentials" → "Create Credentials" → "Service Account"
- Fill in the service account details
- Create and download the JSON key file
2. Configure Service Account
The package automatically uses Service Account authentication when configured through the laravel-google-sheets
dependency. Place your service account JSON file in your Laravel storage directory.
Even for service accounts, scopes must be set in config/google.php
:
'scopes' => [\Google\Service\Webmasters::WEBMASTERS],
3. Environment Variables for Service Account
Option 1: Using File Path
GOOGLE_SERVICE_ENABLED=true GOOGLE_SERVICE_ACCOUNT_JSON_LOCATION=/path/to/service-account.json
Option 2: Using JSON String
GOOGLE_SERVICE_ACCOUNT_JSON_LOCATION
can be an array other than a file path, so you can set it as a JSON string in .env
and decode it to an array in config/google.php
.
// config/google.php 'service' => [ 'file' => json_decode(env('GOOGLE_SERVICE_ACCOUNT_JSON_LOCATION', ''), true), ],
// .env GOOGLE_SERVICE_ACCOUNT_JSON_LOCATION='{"type": "service_account", "project_id": "your-project", "private_key_id": "...", "private_key": "...", "client_email": "...", "client_id": "...", "auth_uri": "...", "token_uri": "...", "auth_provider_x509_cert_url": "...", "client_x509_cert_url": "..."}'
This method is particularly well-suited for GitHub Actions deployment, as it allows the entire service account credentials to be stored as a single secret.
4. Add Service Account to Search Console
- Go to Google Search Console
- Select your property
- Go to Settings → Users and permissions
- Add your service account email as a user with "Full" permissions
Creating Custom Queries
All query classes extend Revolution\Google\SearchConsole\Query\AbstractQuery
, which is a subclass of Google\Service\Webmasters\SearchAnalyticsQueryRequest
. This provides access to all Google Search Console API query parameters.
Generate Query Classes
Use the Artisan command to create new query classes in app/Search
:
php artisan make:search:query YourQueryName
Query Structure
Each query class must implement the init()
method where you define your query parameters:
<?php namespace App\Search; use Revolution\Google\SearchConsole\Query\AbstractQuery; class YourQueryName extends AbstractQuery { public function init(): void { // Define your query parameters here } }
Available Query Parameters
You can use any of the following methods in your init()
method:
Date Range
$this->setStartDate('2024-01-01'); // Start date (YYYY-MM-DD) $this->setEndDate('2024-01-31'); // End date (YYYY-MM-DD) $this->setStartDate(now()->subDays(30)->toDateString()); // Dynamic dates
Dimensions (group results by)
$this->setDimensions(['query']); // Group by search queries $this->setDimensions(['page']); // Group by pages $this->setDimensions(['country']); // Group by countries $this->setDimensions(['device']); // Group by device types $this->setDimensions(['searchAppearance']); // Group by search appearance $this->setDimensions(['query', 'page']); // Multiple dimensions
Filters
// Filter by specific queries $this->setDimensionFilterGroups([ [ 'filters' => [ [ 'dimension' => 'query', 'operator' => 'contains', 'expression' => 'laravel' ] ] ] ]); // Filter by specific pages $this->setDimensionFilterGroups([ [ 'filters' => [ [ 'dimension' => 'page', 'operator' => 'equals', 'expression' => 'https://example.com/page' ] ] ] ]);
Additional Options
$this->setRowLimit(100); // Limit results (max 25,000) $this->setStartRow(0); // Pagination offset $this->setAggregationType(['auto']); // Aggregation type $this->setDataState('final'); // 'final' or 'fresh' data
Example Query Classes
Top Pages Query
<?php namespace App\Search; use Revolution\Google\SearchConsole\Query\AbstractQuery; class TopPagesQuery extends AbstractQuery { public function init(): void { $this->setStartDate(now()->subDays(30)->toDateString()); $this->setEndDate(now()->toDateString()); $this->setDimensions(['page']); $this->setRowLimit(50); $this->setDataState('final'); } }
Mobile Search Queries
<?php namespace App\Search; use Revolution\Google\SearchConsole\Query\AbstractQuery; class MobileQueriesQuery extends AbstractQuery { public function init(): void { $this->setStartDate(now()->subDays(7)->toDateString()); $this->setEndDate(now()->toDateString()); $this->setDimensions(['query']); // Filter for mobile devices only $this->setDimensionFilterGroups([ [ 'filters' => [ [ 'dimension' => 'device', 'operator' => 'equals', 'expression' => 'mobile' ] ] ] ]); $this->setRowLimit(100); } }
Country Performance Query
<?php namespace App\Search; use Revolution\Google\SearchConsole\Query\AbstractQuery; class CountryPerformanceQuery extends AbstractQuery { public function init(): void { $this->setStartDate(now()->subMonths(3)->toDateString()); $this->setEndDate(now()->toDateString()); $this->setDimensions(['country', 'query']); $this->setRowLimit(200); $this->setAggregationType(['auto']); } }
Basic Usage
Using OAuth 2.0 Authentication
When using OAuth, you'll need to obtain access tokens through the Socialite authentication flow, then use setAccessToken()
to authenticate your requests.
1. OAuth Authentication Flow (Controller Example)
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Laravel\Socialite\Facades\Socialite; use Revolution\Google\SearchConsole\Facades\SearchConsole; use App\Search\TopPagesQuery; class GoogleSearchConsoleController extends Controller { public function redirectToGoogle() { return Socialite::driver('google') ->scopes(config('google.scopes')) ->with([ 'access_type' => config('google.access_type'), 'prompt' => config('google.prompt'), ]) ->redirect(); } public function handleGoogleCallback() { $user = Socialite::driver('google')->user(); // Store tokens in your database auth()->user()->update([ 'google_access_token' => $user->token, 'google_refresh_token' => $user->refreshToken, ]); return redirect()->route('dashboard'); } public function getSearchConsoleData(Request $request) { $token = [ 'access_token' => auth()->user()->google_access_token, 'refresh_token' => auth()->user()->google_refresh_token, ]; // Create and execute query $query = new TopPagesQuery(); $result = SearchConsole::setAccessToken($token) ->query('https://example.com/', $query); return response()->json($result); } }
2. Using OAuth with Model Integration
You can use the WithSearchConsole
trait to integrate Search Console functionality directly into your models:
<?php namespace App\Models; use Illuminate\Foundation\Auth\User as Authenticatable; use Revolution\Google\SearchConsole\Traits\WithSearchConsole; class User extends Authenticatable { use WithSearchConsole; protected $fillable = [ 'name', 'email', 'google_access_token', 'google_refresh_token' ]; protected function tokenForSearchConsole(): array { return [ 'access_token' => $this->google_access_token, 'refresh_token' => $this->google_refresh_token, ]; } }
Then use it in your application:
use App\Search\TopPagesQuery; $user = User::find(1); $query = new TopPagesQuery(); // The searchconsole() method automatically handles authentication $result = $user->searchconsole()->query('https://example.com/', $query); $sites = $user->searchconsole()->listSites();
Using Service Account Authentication
With Service Account authentication, the package automatically handles authentication through the GoogleApiClient included in laravel-google-sheets
. No manual token management is required.
Service Account Usage Example
<?php namespace App\Services; use Revolution\Google\SearchConsole\Facades\SearchConsole; use App\Search\TopPagesQuery; use App\Search\MobileQueriesQuery; use App\Search\CountryPerformanceQuery; class SearchConsoleService { public function getTopPages(string $siteUrl): object { $query = new TopPagesQuery(); // Service Account authentication is automatic return SearchConsole::query($siteUrl, $query); } public function getMobileQueries(string $siteUrl): object { $query = new MobileQueriesQuery(); return SearchConsole::query($siteUrl, $query); } public function getCountryPerformance(string $siteUrl): object { $query = new CountryPerformanceQuery(); return SearchConsole::query($siteUrl, $query); } public function getAllSites(): object { return SearchConsole::listSites(); } public function getSiteInfo(string $siteUrl): object { return SearchConsole::listSites(['siteUrl' => $siteUrl]); } }
Using Service Account in Commands
<?php namespace App\Console\Commands; use Illuminate\Console\Command; use Revolution\Google\SearchConsole\Facades\SearchConsole; use App\Search\TopPagesQuery; class FetchSearchConsoleData extends Command { protected $signature = 'searchconsole:fetch {site_url}'; protected $description = 'Fetch Search Console data for a site'; public function handle() { $siteUrl = $this->argument('site_url'); $query = new TopPagesQuery(); try { $result = SearchConsole::query($siteUrl, $query); $this->info("Found {$result->rowCount} results:"); foreach ($result->rows as $row) { $this->line("Page: {$row->keys[0]}"); $this->line(" Clicks: {$row->clicks}"); $this->line(" Impressions: {$row->impressions}"); $this->line(" CTR: " . round($row->ctr * 100, 2) . "%"); $this->line(" Position: " . round($row->position, 1)); $this->line(""); } } catch (\Exception $e) { $this->error("Error fetching data: " . $e->getMessage()); } } }
Quick Reference
Available Facade Methods
use Revolution\Google\SearchConsole\Facades\SearchConsole; // Set access token (OAuth only) SearchConsole::setAccessToken($token); // Execute a query SearchConsole::query($siteUrl, $queryObject); // List all sites SearchConsole::listSites(); // List sites with parameters SearchConsole::listSites(['siteUrl' => 'https://example.com/']);
Response Structure
The API returns objects with the following structure:
$result = SearchConsole::query($siteUrl, $query); // Access the data echo $result->rowCount; // Number of results echo $result->responseAggregationType; // Aggregation type used foreach ($result->rows as $row) { print_r($row->keys); // Array of dimension values echo $row->clicks; // Number of clicks echo $row->impressions; // Number of impressions echo $row->ctr; // Click-through rate (0-1) echo $row->position; // Average position (1+) }
Laravel Helper Functions for Response Handling
The response objects work seamlessly with Laravel's helper functions, making data manipulation more convenient:
Using data_get()
for Safe Property Access
$result = SearchConsole::query($siteUrl, $query); // Safely access properties with default values $rowCount = data_get($result, 'rowCount', 0); $aggregationType = data_get($result, 'responseAggregationType', 'unknown'); // Access nested properties safely foreach ($result->rows as $row) { $clicks = data_get($row, 'clicks', 0); $impressions = data_get($row, 'impressions', 0); $ctr = data_get($row, 'ctr', 0.0); $position = data_get($row, 'position', 0.0); // Handle optional or nested properties $firstKey = data_get($row, 'keys.0', 'N/A'); }
Using collect()
for Enhanced Data Processing
$result = SearchConsole::query($siteUrl, $query); // Convert rows to a Laravel Collection for powerful data manipulation $rows = collect($result->rows); // Filter rows with high CTR $highCtrRows = $rows->filter(fn($row) => $row->ctr > 0.05); // Sort by impressions (descending) $sortedRows = $rows->sortByDesc('impressions'); // Get top 10 pages by clicks $topPages = $rows->sortByDesc('clicks')->take(10); // Transform data structure $pageData = $rows->map(function ($row) { return [ 'page' => data_get($row, 'keys.0', 'Unknown'), 'metrics' => [ 'clicks' => $row->clicks, 'impressions' => $row->impressions, 'ctr' => round($row->ctr * 100, 2) . '%', 'position' => round($row->position, 1), ] ]; }); // Group by device type (if querying by device dimension) $deviceGroups = $rows->groupBy(fn($row) => data_get($row, 'keys.0', 'unknown')); // Calculate totals $totalClicks = $rows->sum('clicks'); $totalImpressions = $rows->sum('impressions'); $averagePosition = $rows->average('position');
LICENSE
MIT