Laravel package for the Unsplash API
mahdimajidzadeh/laravel-unsplash is a Laravel package for laravel package for the unsplash api.
It currently has 11 GitHub stars and 11.363 downloads on Packagist (latest version v0.1.5).
Install it with composer require mahdimajidzadeh/laravel-unsplash.
Discover more Laravel packages by mahdimajidzadeh
or browse all Laravel packages to compare alternatives.
Last updated
A Laravel package covering the whole Unsplash API: photos, users, the logged-in user, collections, topics, search, stats and the user authentication (OAuth) workflow.
Via Composer
$ composer require mahdimajidzadeh/laravel-unsplash
If you do not run Laravel 5.5 (or higher), then add the service provider and the
facade alias in config/app.php:
'providers' => [
MahdiMajidzadeh\LaravelUnsplash\LaravelUnsplashServiceProvider::class,
],
'aliases' => [
'Unsplash' => MahdiMajidzadeh\LaravelUnsplash\Facades\Unsplash::class,
],
On Laravel 5.5+ package auto-discovery takes care of both.
Publishing the configuration is optional — the package ships with defaults — but useful if you want to tweak them:
$ php artisan vendor:publish --tag=unsplash-config
That copies the defaults to config/unsplash.php.
Register an application at unsplash.com/oauth/applications
and add your keys to .env:
UNSPLASH_ACCESS_KEY=your-access-key
# Only needed for the user authentication (OAuth) workflow
UNSPLASH_SECRET_KEY=your-secret-key
UNSPLASH_REDIRECT_URI=https://your-app.test/unsplash/callback
# Optional: act on behalf of a single user on every request
UNSPLASH_ACCESS_TOKEN=
The legacy ApplicationID configuration key is still honoured, so
configuration files published by older versions of this package keep working.
Everything is reachable from the Unsplash facade:
use MahdiMajidzadeh\LaravelUnsplash\Facades\Unsplash;
$photos = Unsplash::photos()->photos(['per_page' => 30])->get();
You can also resolve MahdiMajidzadeh\LaravelUnsplash\Unsplash from the
container, or instantiate a single resource directly — as in previous versions
of this package:
$unsplash = new MahdiMajidzadeh\LaravelUnsplash\Photo();
$photos = $unsplash->photos()->get();
Every endpoint method performs the request and returns the resource, so the result can be read with:
| Method | Returns |
|------------------------|---------------------------------------------------------------|
| get() | the decoded body (stdClass or array of stdClass) |
| getArray() | the decoded body cast to an array |
| toArray() | the decoded body as a nested array |
| raw() | the raw JSON body |
| status() | the HTTP status code |
| headers() | all response headers |
| totalItems() | total number of items available |
| totalPages() | total number of pages available |
| links() | the parsed Link header (first, prev, next, last) |
| rateLimit() | requests allowed per hour |
| rateLimitRemaining() | requests left for the current hour |
| response() | the response object, for anything else |
$photos = Unsplash::photos()->photos(['page' => 2, 'per_page' => 30]);
$photos->get(); // the photos
$photos->totalPages(); // 1234
$photos->response()->nextPage(); // 3
$photos->rateLimitRemaining(); // 987
See the Unsplash documentation for the
parameters accepted by each endpoint; they are passed straight through as the
$params array, with a few conveniences:
null values are dropped, so you can pass optional parameters unconditionally.true/false strings Unsplash expects.['collections' => [123, 456]] becomes collections=123,456.
An empty list is left out entirely.location and exif on a photo update keep their
keys.$photos = Unsplash::photos();
$photos->photos($params)->get(); // GET /photos
$photos->single($id, $params)->get(); // GET /photos/:id
$photos->random($params)->get(); // GET /photos/random
$photos->statistics($id, $params)->get(); // GET /photos/:id/statistics
$photos->download($id); // GET /photos/:id/download — returns the URL
$photos->trackDownload($id)->get(); // GET /photos/:id/download — chainable
$photos->update($id, $params)->get(); // PUT /photos/:id (write_photos)
$photos->like($id)->get(); // POST /photos/:id/like (write_likes)
$photos->unlike($id); // DELETE /photos/:id/like (write_likes)
all() is an alias of photos(), find() of single() and statistic() of
statistics().
Unsplash requires download($id) (or trackDownload($id)) to be called
whenever your application downloads a photo, so the photographer gets credited.
getID() and getURL() are available on any response holding photos — a
single photo, a list, or search results:
Unsplash::photos()->random()->getID(); // WLUHO9A_xik
Unsplash::photos()->random()->getURL(); // 1600x900, cropped
Unsplash::photos()->random()->getURL(800, 600);
// https://images.unsplash.com/photo-1417325384643-aac51acc9e5d?ixid=...&w=800&h=600&fit=crop
Unsplash serves its images through Imgix, so getURL() takes the raw url from the
api response and appends the sizing parameters to it. Use getSizedURL() to pick one
of the sizes Unsplash returns as-is, or to pass your own Imgix parameters:
Unsplash::photos()->random()->getSizedURL('regular'); // raw, full, regular, small, thumb
Unsplash::photos()->random()->getSizedURL('raw', ['w' => 800]);
All three methods return null when the response holds no photo.
$users = Unsplash::users();
$users->single($username, $params)->get(); // GET /users/:username
$users->portfolio($username); // GET /users/:username/portfolio — returns the URL
$users->photos($username, $params)->get(); // GET /users/:username/photos
$users->likes($username, $params)->get(); // GET /users/:username/likes
$users->collections($username, $params)->get(); // GET /users/:username/collections
$users->statistics($username, $params)->get(); // GET /users/:username/statistics
find() is an alias of single() and statistic() of statistics().
These endpoints need a bearer token — see User authentication.
$me = Unsplash::withAccessToken($token)->me();
$me->profile()->get(); // GET /me (read_user)
$me->update($params)->get(); // PUT /me (write_user)
$collections = Unsplash::collections();
$collections->collections($params)->get(); // GET /collections
$collections->single($id, $params)->get(); // GET /collections/:id
$collections->photos($id, $params)->get(); // GET /collections/:id/photos
$collections->related($id)->get(); // GET /collections/:id/related
$collections->create($params)->get(); // POST /collections (write_collections)
$collections->update($id, $params)->get(); // PUT /collections/:id (write_collections)
$collections->delete($id); // DELETE /collections/:id (write_collections)
$collections->addPhoto($id, $photoId)->get(); // POST /collections/:collection_id/add (write_collections)
$collections->removePhoto($id, $photoId); // DELETE /collections/:collection_id/remove (write_collections)
create() also accepts the title directly:
Unsplash::withAccessToken($token)->collections()->create('Good dogs', 'A description', false);
all() is an alias of collections() and find() of single().
$topics = Unsplash::topics();
$topics->topics($params)->get(); // GET /topics
$topics->single($idOrSlug, $params)->get(); // GET /topics/:id_or_slug
$topics->photos($idOrSlug, $params)->get(); // GET /topics/:id_or_slug/photos
all() is an alias of topics() and find() of single().
$search = Unsplash::search();
$search->photo($query, $params)->get(); // GET /search/photos
$search->collection($query, $params)->get(); // GET /search/collections
$search->user($query, $params)->get(); // GET /search/users
photos(), collections() and users() are aliases of the above. Search
responses wrap their matches, so results() returns them without the
surrounding counters:
$search = Unsplash::search()->photo('dogs', ['orientation' => 'landscape']);
$search->results(); // the photos
$search->totalItems(); // 1337
$search->totalPages(); // 134
$stats = Unsplash::stats();
$stats->total()->get(); // GET /stats/total
$stats->month()->get(); // GET /stats/month
Public requests are authenticated with your access key. To read private data or act on behalf of a user, send them through the OAuth workflow and use the resulting bearer token.
use MahdiMajidzadeh\LaravelUnsplash\Facades\Unsplash;
// 1. Send the user to Unsplash to authorize your application
Route::get('unsplash/redirect', function () {
return Unsplash::oauth()->redirect(['read_user', 'write_likes']);
// or build the URL yourself:
// return redirect(Unsplash::oauth()->authorizeUrl(['read_user'], null, $state));
});
// 2. Exchange the code Unsplash sends back for an access token
Route::get('unsplash/callback', function (Illuminate\Http\Request $request) {
$token = Unsplash::oauth()->accessToken($request->query('code'));
// 3. Use it — access tokens do not expire
return Unsplash::withAccessToken($token)->me()->profile()->get();
});
requestToken($code) gives you the whole token response (access_token,
token_type, scope, created_at) instead of just the token.
Available scopes are listed in OAuth::SCOPES: public, read_user,
write_user, read_photos, write_photos, write_likes,
write_followers, read_collections, write_collections. The default scopes
used by redirect() and authorizeUrl() come from the unsplash.scopes
configuration entry.
withAccessToken($token) returns a copy, so the shared instance keeps using
your access key. It is available on the Unsplash entry point and on every
resource:
$collections = Unsplash::collections()->withAccessToken($token);
$collections->create('Good dogs');
Non 2xx responses throw an exception carrying the status code and the messages Unsplash returned:
use MahdiMajidzadeh\LaravelUnsplash\Exceptions\NotFoundException;
use MahdiMajidzadeh\LaravelUnsplash\Exceptions\UnsplashException;
try {
Unsplash::photos()->single('does-not-exist')->get();
} catch (NotFoundException $e) {
$e->status(); // 404
$e->errors(); // ['Couldn't find Photo']
} catch (UnsplashException $e) {
// any other API error
}
UnauthorizedException (401), ForbiddenException (403), NotFoundException
(404), ValidationException (422) and RateLimitException (429) all extend
UnsplashException, which extends RuntimeException.
Requests that never reach the API — DNS failures, timeouts, refused connections
— throw a ConnectionException, which extends UnsplashException too. Catching
UnsplashException therefore covers every failure mode, and
$e->getPrevious() gives you the underlying Guzzle exception.
Unsplash::fake() answers from a queue of responses instead of calling the API.
When the container is booted it replaces the bound instance, so the facade and
anything type hinting Unsplash receive the fake too:
use MahdiMajidzadeh\LaravelUnsplash\Facades\Unsplash;
public function test_it_shows_a_random_photo()
{
$unsplash = Unsplash::fake([
['id' => 'abc123', 'urls' => ['raw' => 'https://images.unsplash.com/photo-1']],
]);
$this->get('/')->assertSee('abc123');
// Every request made against the fake is recorded.
$this->assertSame(1, $unsplash->recordedCount());
$this->assertSame('/photos/random', $unsplash->recordedRequest()->getUri()->getPath());
}
Plain arrays become 200 responses. Use FakeResponse when you need to control
the status code, the headers or the pagination metadata:
use MahdiMajidzadeh\LaravelUnsplash\Testing\FakeResponse;
Unsplash::fake([
FakeResponse::make(['id' => 'abc123'], 200, ['X-Ratelimit-Remaining' => '12']),
FakeResponse::error("Couldn't find Photo", 404),
FakeResponse::paginated([['id' => 'a'], ['id' => 'b']], 40, 10),
]);
Responses are returned in order, and Unsplash::fake() returns the fake so you
can inspect it. Requests are recorded as PSR-7 request objects, readable with
recorded(), recordedRequest($index) and recordedCount().
Photo::curated(), Collection::curated() and Collection::featured() are
kept for backwards compatibility, but Unsplash retired those endpoints — use
the topic endpoints instead. The Unsplush base class is likewise kept as an
alias of Endpoint.
Run the test suite with:
$ composer test
Code style is enforced with Pint. It needs PHP 8.2+, so it is not a development dependency of this package — install it globally and run it from the package root:
$ composer global require laravel/pint
$ pint --test
See CHANGELOG.md.
The MIT License (MIT). See LICENSE for details.