A Laravel package providing seamless integration with the Google Photos Library API and Google Photos Picker API. Upload photos, manage albums, and interact with Google Photos directly from your Laravel applications.
This package enables Laravel applications to:
- Upload photos to Google Photos with automatic media item creation
- Manage albums - create, list, and update album information
- List media items and search through uploaded content
- Use Google Photos Picker to let users select photos from their Google Photos library
- OAuth 2.0 authentication with proper token management and refresh handling
Important: Due to Google Photos API limitations, you can only access and manage content that was uploaded via your application. For accessing existing user photos, use the Google Photos Picker API.
composer require revolution/laravel-google-photos
php artisan vendor:publish --tag="google-config"
- Visit the Google Cloud Console
- Enable Photos Library API and Google Photos Picker API
⚠️ Be careful not to select "Google Picker API" (different from Photos Picker API) - Create OAuth 2.0 client credentials
- Set authorized redirect URIs for your application
Add to your config/services.php
:
'google' => [
'client_id' => env('GOOGLE_CLIENT_ID', ''),
'client_secret' => env('GOOGLE_CLIENT_SECRET', ''),
'redirect' => env('GOOGLE_REDIRECT', ''),
],
Edit config/google.php
:
<?php
return [
'client_id' => env('GOOGLE_CLIENT_ID', ''),
'client_secret' => env('GOOGLE_CLIENT_SECRET', ''),
'redirect_uri' => env('GOOGLE_REDIRECT', ''),
'scopes' => [
'https://www.googleapis.com/auth/photoslibrary.appendonly',
'https://www.googleapis.com/auth/photoslibrary.readonly.appcreateddata',
'https://www.googleapis.com/auth/photoslibrary.edit.appcreateddata',
'https://www.googleapis.com/auth/photospicker.mediaitems.readonly',
],
'access_type' => 'offline',
'prompt' => 'consent select_account',
];
Add to your .env
:
GOOGLE_CLIENT_ID=your_client_id_here
GOOGLE_CLIENT_SECRET=your_client_secret_here
GOOGLE_REDIRECT=https://yourapp.com/auth/google/callback
use Revolution\Google\Photos\Facades\Photos;
// Upload a photo (two-step process)
$uploadToken = Photos::withToken($user->refresh_token)
->upload($fileContent, $fileName);
$result = Photos::batchCreate([$uploadToken]);
- PHP >= 8.2
- Laravel >= 11.0
composer require revolution/laravel-google-photos
php artisan vendor:publish --tag="google-config"
This package ONLY supports OAuth 2.0 authentication.
❌ Service Account authentication is NOT supported
❌ API Key authentication is NOT supported
Why OAuth 2.0 Only? The Google Photos API is designed for user-centric applications and requires user consent to access personal photo libraries. Google Photos API does not support Service Account or API Key authentication methods because:
- Privacy by Design: Photos are personal data requiring explicit user consent
- Google API Limitation: The Photos Library API only accepts OAuth 2.0 tokens
- User Context Required: All operations need to be performed on behalf of a specific user
- Go to Google Cloud Console
- Create a new project or select existing one
- Enable these APIs:
- Photos Library API (for uploading and managing photos)
- Google Photos Picker API (for photo selection interface)
- Go to "Credentials" → "Create Credentials" → "OAuth 2.0 Client ID"
- Choose "Web application"
- Add your authorized redirect URIs (e.g.,
https://yourapp.com/auth/google/callback
)
The 'access_type' => 'offline'
setting is required to obtain refresh tokens for long-term access.
// routes/web.php
Route::get('/auth/google', [AuthController::class, 'redirect']);
Route::get('/auth/google/callback', [AuthController::class, 'callback']);
// AuthController.php
use Laravel\Socialite\Facades\Socialite;
public function redirect()
{
return Socialite::driver('google')
->scopes(config('google.scopes'))
->with([
'access_type' => config('google.access_type'),
'prompt' => config('google.prompt'),
])
->redirect();
}
public function callback()
{
$user = Socialite::driver('google')->user();
$loginUser = User::updateOrCreate(
['email' => $user->email],
[
'name' => $user->name,
'refresh_token' => $user->refreshToken, // Store this!
]
);
auth()->login($loginUser);
return redirect('/home');
}
Photos must be uploaded using a two-step process:
use Revolution\Google\Photos\Facades\Photos;
use Illuminate\Http\Request;
public function uploadPhoto(Request $request)
{
$file = $request->file('photo');
// Step 1: Upload file content and get upload token
$uploadToken = Photos::withToken($request->user()->refresh_token)
->upload($file->getContent(), $file->getClientOriginalName());
// Step 2: Create media item from upload token
$result = Photos::batchCreate([$uploadToken]);
return response()->json($result);
}
use Revolution\Google\Photos\Facades\Photos;
public function listPhotos()
{
$mediaItems = Photos::withToken(auth()->user()->refresh_token)
->listMediaItems();
foreach ($mediaItems as $item) {
echo $item->getBaseUrl() . "\n";
echo $item->getFilename() . "\n";
}
}
use Revolution\Google\Photos\Facades\Photos;
use Google\Photos\Library\V1\PhotosLibraryResourceFactory;
public function createAlbum()
{
$newAlbum = Photos::withToken(auth()->user()->refresh_token)
->createAlbum(PhotosLibraryResourceFactory::album('My New Album'));
return [
'id' => $newAlbum->getId(),
'title' => $newAlbum->getTitle(),
'url' => $newAlbum->getProductUrl(),
];
}
use Revolution\Google\Photos\Facades\Photos;
public function listAlbums()
{
$albums = Photos::withToken(auth()->user()->refresh_token)
->listAlbums();
foreach ($albums as $album) {
echo "Album: " . $album->getTitle() . "\n";
echo "ID: " . $album->getId() . "\n";
echo "Items: " . $album->getMediaItemsCount() . "\n";
}
}
use Revolution\Google\Photos\Facades\Photos;
public function updateAlbumTitle($albumId, $newTitle)
{
$album = Photos::withToken(auth()->user()->refresh_token)
->updateAlbumTitle($albumId, $newTitle);
return $album;
}
Add the PhotosLibrary
trait to your User model for cleaner syntax:
use Revolution\Google\Photos\Traits\PhotosLibrary;
class User extends Authenticatable
{
use PhotosLibrary;
protected function tokenForPhotoLibrary(): array|string
{
return $this->refresh_token;
}
}
// Usage with trait
$albums = $user->photos()->listAlbums();
$uploadToken = $user->photos()->upload($content, $filename);
Use the Picker API to let users select existing photos from their Google Photos library:
use Revolution\Google\Photos\Facades\Picker;
use Revolution\Google\Photos\Support\Token;
// Get access token from refresh token
$accessToken = Token::toAccessToken(auth()->user()->refresh_token);
// Create picker session
$picker = Picker::withToken($accessToken)->create();
// Redirect user to picker
return redirect($picker['pickerUri']);
// Later, check if user finished selecting
$session = Picker::withToken($accessToken)->get($picker['id']);
if ($session['mediaItemsSet']) {
// Get selected media items
$mediaItems = Picker::withToken($accessToken)->list($picker['id']);
}
This package delegates to the Google Photos Library PHP client and supports all its methods:
use Revolution\Google\Photos\Facades\Photos;
// Get specific album
$album = Photos::withToken($token)->getAlbum($albumId);
// Search media items
$searchResults = Photos::withToken($token)->searchMediaItems($searchRequest);
// Add media items to album
$photos = Photos::withToken($token)->batchAddMediaItemsToAlbum($albumId, $mediaItemIds);
Methods like listMediaItems()
and listAlbums()
return a PagedListResponse
for handling large result sets:
use Revolution\Google\Photos\Facades\Photos;
use Google\ApiCore\PagedListResponse;
$items = Photos::withToken($token)->listMediaItems();
// Iterate through all items (handles pagination automatically)
foreach ($items as $item) {
echo $item->getBaseUrl() . "\n";
}
// Or access page information
$page = $items->getPage();
echo "Page token: " . $page->getNextPageToken();
use Revolution\Google\Photos\Facades\Photos;
use Google\ApiCore\ApiException;
try {
$result = Photos::withToken($token)->upload($content, $filename);
} catch (ApiException $e) {
Log::error('Google Photos API error: ' . $e->getMessage());
return response()->json(['error' => 'Upload failed'], 500);
}
You can extend the Photos facade with custom methods:
// In AppServiceProvider::boot()
use Revolution\Google\Photos\Facades\Photos;
Photos::macro('customUpload', function ($file) {
$uploadToken = $this->upload($file->getContent(), $file->getClientOriginalName());
return $this->batchCreate([$uploadToken]);
});
// Usage
$result = Photos::withToken($token)->customUpload($file);
No. Service Account authentication is not supported by the Google Photos API. The API requires OAuth 2.0 user consent because it deals with personal photo data.
No. API Key authentication is not supported by the Google Photos API. Only OAuth 2.0 authentication is available.
Google Photos API is designed exclusively for user-centric applications. Since photos are personal data, Google requires explicit user consent through OAuth 2.0. This ensures users maintain control over their photo data and can revoke access at any time.
No, not directly. The Google Photos Library API only allows access to photos uploaded via your application. To work with existing user photos, you must use the Google Photos Picker API included in this package.
If you're only uploading to your own account during development, no review is needed. However, for production use with other users' accounts, Google requires app review for sensitive scopes.
Google Photos API has rate limits and quotas. See the official documentation for current limits.
- laravel-google-sheets - Google Sheets API integration
- laravel-google-searchconsole - Google Search Console API integration
- Google Photos Library API Documentation
- Google Photos Picker API Documentation
- Laravel Socialite Documentation
- Package Documentation
MIT