tomshaw/google-api
Laravel Google OAuth 2.0 service client with configurable token storage (DB or custom), published config, and migrations. Integrates google/apiclient-services and supports Composer cleanup to include only the Google APIs you need (e.g., Gmail, Calendar).
Install the package:
composer require tomshaw/google-api
Add the pre-autoload script to composer.json to optimize API service loading:
"scripts": {
"pre-autoload-dump": "Google\\Task\\Composer::cleanup"
}
Specify required Google APIs in extra:
"extra": {
"google/apiclient-services": ["Gmail", "Calendar"]
}
Publish config:
php artisan vendor:publish --provider="TomShaw\GoogleApi\Providers\GoogleApiServiceProvider" --tag=config
Configure config/google-api.php with:
auth_config: Path to your Google OAuth credentials JSON.service_scopes: Required scopes (e.g., ['https://www.googleapis.com/auth/calendar']).token_storage_adapter: Defaults to DatabaseTokenStorage (run migrations if used).First OAuth Flow:
// routes/web.php
Route::get('/auth/google', [GoogleAuthController::class, 'index']);
Route::get('/auth/google/callback', [GoogleAuthController::class, 'callback']);
use TomShaw\GoogleApi\Services\CalendarService;
class CalendarController extends Controller
{
public function listEvents(CalendarService $calendar)
{
$events = $calendar->listEvents('primary');
return response()->json($events);
}
}
GoogleClient::createAuthUrl() to generate OAuth URLs with custom scopes.
$authUrl = $client->createAuthUrl(['https://www.googleapis.com/auth/gmail.readonly']);
$accessToken = $client->fetchAccessTokenWithAuthCode($request->get('code'));
$client->setAccessToken($accessToken);
CalendarService, GmailService) into controllers:
public function __construct(private CalendarService $calendar) {}
$calendar->events()
->setMaxResults(10)
->setTimeMin('2023-01-01T00:00:00Z')
->list();
StorageAdapterInterface for custom storage (e.g., Redis):
class RedisTokenStorage implements StorageAdapterInterface {
public function save($token) { /* ... */ }
public function find($userId) { /* ... */ }
}
Update config:
'token_storage_adapter' => App\Services\RedisTokenStorage::class,
$client->setAccessToken($token)->fetch(); // Auto-refreshes if needed
public function handle(Request $request, Closure $next) {
if (!$client->getAccessToken()) {
return redirect()->route('auth.google');
}
return $next($request);
}
Google_Service_Exception for API-specific errors:
try {
$calendar->events()->list();
} catch (Google_Service_Exception $e) {
Log::error($e->getMessage());
}
API Service Bloat:
composer.json extra to avoid loading unused APIs.Google\Task\Composer::cleanup to prune unused services.Token Expiry:
access_type: 'offline' and approval_prompt: 'auto' in config to enable refresh tokens.Scope Mismatches:
service_scopes in config match those requested in createAuthUrl().Database Storage:
DatabaseTokenStorage, ensure the google_api_tokens table exists (run migrations).database/migrations/.GOOGLE_APPLICATION_CREDENTIALS environment variable to log API requests:
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/credentials.json
try {
$token = $client->getAccessToken();
} catch (Exception $e) {
Log::error('Token fetch failed: ' . $e->getMessage());
}
GoogleService to add domain-specific methods:class CustomCalendarService extends GoogleService {
public function getUserEvents($userId) {
return $this->client->calendar->events->listCalendarEvents($userId);
}
}
public function handle($request, Closure $next) {
$response = $next($request);
return $response->header('X-Google-API', 'Processed');
}
public function handle(TokenRefreshed $event) {
Log::info('Token refreshed for user: ' . $event->userId);
}
if ($request->has('calendar')) {
$calendar = app(CalendarService::class);
}
setMaxResults() and pagination for large datasets:
$events = $calendar->events()->setMaxResults(100)->list();
while ($events->getNextPageToken()) {
$events = $calendar->events()->setPageToken($events->getNextPageToken())->list();
}
https://www.googleapis.com/auth/userinfo.profile if unused).$client->deleteAccessToken(); // Clears stored token
How can I help you explore Laravel packages today?