corollarium / modelarium-medialibrary
Integrates Spatie Laravel-media-library with Modelarium and GraphQL
Requires
- php: >=7.2.0
- corollarium/modelarium: ^0.4
- illuminate/support: ^7.16
- nuwave/lighthouse: ^5.1
- webonyx/graphql-php: ^14.5
Requires (Dev)
- phpstan/phpstan: ^0.12
- phpunit/phpunit: ^8
- thecodingmachine/phpstan-safe-rule: ^1.0
Suggests
- spatie/laravel-medialibrary: The base medialibrary should be installed too.
README
This package integrates Spatie's Laravel Media Library to Modelarium, allowing GraphQL queries with LighthousePHP to easily access media on models.
Installing
On your Laravel app run:
composer require corollarium/modelarium-medialibrary
Quick overview
This a Graphql file for a model with two different media collections, "image" and "map". Both have extra fields, "url" and "description". "Map" also has a conversion "thumb" with 150x150 (max, maintaining aspect ratio) thumbnails.
type Post @migrationSoftDeletes @migrationTimestamps { id: ID! name: String! @modelFillable @renderable( label: "Name" size: "large" itemtype: "name" card: true title: true ) description: Text! @modelFillable @renderable(label: "Description", itemtype: "description") imageUrl: Url @migrationSkip image: LaravelMediaLibraryData @migrationSkip @laravelMediaLibraryData( collection: "image" fields: ["url", "description"] ) map: LaravelMediaLibraryData @migrationSkip @laravelMediaLibraryData( collection: "map" fields: ["url", "description"] conversions: [ { name: "thumb", width: 150, height: 150 } ] ) }
This will automatically generate roughly the following code on your model class:
/** * This file was automatically generated by Modelarium on 2020-12-13T18:28:11+00:00 */ namespace App\Models; abstract class BasePost extends \Illuminate\Database\Eloquent\Model implements \Spatie\MediaLibrary\HasMedia { use \Spatie\MediaLibrary\InteractsWithMedia; // ...lots of base stuff... /** * Configures Laravel media-library */ public function registerMediaCollections(): void { $this->addMediaCollection('image'); $this->addMediaCollection('map'); } /** * Returns a collection media from Laravel-MediaLibrary */ public function getMediaImageCollection(): \Spatie\MediaLibrary\MediaCollections\Models\Collections\MediaCollection { return $this->getMedia('image'); } /** * Returns custom fields for the media */ public function getMediaImageCustomFields(): array { return ['url', 'description']; } /** * Returns the media attribute (url) for the image */ public function getImageUrlAttribute(): string { $image = $this->getMediaImageCollection()->first(); if ($image) { return $image->getUrl(); } return ''; } /** * Returns media attribute for the image media with custom fields */ public function getImageAttribute(): array { $image = $this->getMediaImageCollection()->first(); if ($image) { $customFields = []; foreach ($this->getMediaImageCustomFields() as $c) { $customFields[$c] = $image->getCustomProperty($c); } return [ 'url' => $image->getUrl(), 'fields' => json_encode($customFields) ]; } return []; } /** * Configures Laravel media-library conversions */ public function registerMediaConversions(?\Spatie\MediaLibrary\MediaCollections\Models\Media $media = null): void { $this->addMediaConversions('thumb')->width('150')->height('150'); } /** * Returns a collection media from Laravel-MediaLibrary */ public function getMediaMapCollection(): \Spatie\MediaLibrary\MediaCollections\Models\Collections\MediaCollection { return $this->getMedia('map'); } /** * Returns custom fields for the media */ public function getMediaMapCustomFields(): array { return ['url', 'description']; } /** * Returns the media attribute (url) for the map */ public function getMapUrlAttribute(): string { $image = $this->getMediaMapCollection()->first(); if ($image) { return $image->getUrl(); } return ''; } /** * Returns media attribute for the map media with custom fields */ public function getMapAttribute(): array { $image = $this->getMediaMapCollection()->first(); if ($image) { $customFields = []; foreach ($this->getMediaMapCustomFields() as $c) { $customFields[$c] = $image->getCustomProperty($c); } return [ 'url' => $image->getUrl(), 'fields' => json_encode($customFields) ]; } return []; } // ...more stuff here... }
Documentation
The @laravelMediaLibraryData
directive supports the following arguments:
TODO
FAQ
I need some custom conversion on my collection.
If the basic arguments on the directive are not enough, just override the method on your model class:
class Post extends BasePost { public function registerMediaConversions(?\Spatie\MediaLibrary\MediaCollections\Models\Media $media = null): void { // do your stuff here } }
How do I get only the image url for a single image in my type?
Define a xxxUrl
on your type, where xxx
is your collection name. which is filled automatically by the attribute method on the model. Don't forget to add @migrationSkip
so it won't be created in the migration. Example:
type Post { # ... # this field is filled automatically by the model imageUrl: Url @migrationSkip image: LaravelMediaLibraryData @migrationSkip @laravelMediaLibraryData( collection: "image" singleFile: true fields: ["url", "description"] ) }
your query will look like this:
query($id: ID!) { post(id: $id) { id # ... other fields imageUrl # this will return the image url } }
How do I get the full image data in a query?
Just define the LaravelMediaLibraryData
field in your type. Example:
type Post { image: [LaravelMediaLibraryData!] @migrationSkip @laravelMediaLibraryData( collection: "image" fields: ["url", "description"] ) }
On your query, fetch the field
query($id: ID!) { post(id: $id) { id # ... other fields image { url # the url, a String fields # a JSON-encoded string with your custom fields if they exist } } }
You can easily access only the first one too:
type Post { image: [LaravelMediaLibraryData!] @migrationSkip @laravelMediaLibraryData( collection: "image" fields: ["url", "description"] ) # add this to get the first one: imageFirst: LaravelMediaLibraryData @migrationSkip }
In your query:
query($id: ID!) { post(id: $id) { id # ... other fields imageFirst { url # the url, a String fields # a JSON-encoded string with your custom fields if they exist } } }
I want a single image in the collection, never more than one. How do I do that?
Declare a singleFile
collection. Notice that the return type here is a single LaravelMediaLibraryData
, not an array:
type Post { image: LaravelMediaLibraryData @migrationSkip @laravelMediaLibraryData( collection: "image" singleFile: true fields: ["url", "description"] ) # the [collection]First attribute is also available, so you can mix it with non-singleFile collections: imageFirst: LaravelMediaLibraryData @migrationSkip }
Then access it normally:
query($id: ID!) { post(id: $id) { id # ... other fields image { url # the url, a String fields # a JSON-encoded string with your custom fields if they exist } } }
How do I add thumbnails?
Add a conversions
field. Two accessor methods will be created on your model for each collection: [CollectionName][ConversionName]HTML
(which generates the full HTML tag) and [CollectionName][ConversionName]Url
(just the URL). Add them to your GraphQL type (and remember to @migrationSkip
them) to access them. Example:
type Post { image: LaravelMediaLibraryData @migrationSkip @eagerLoad(name: "media") @laravelMediaLibraryData( collection: "image" conversions: [{ name: "thumb", width: 128, height: 128 }] ) imageThumbHTML: String @migrationSkip @renderable(show: true) imageThumbUrl: Url @migrationSkip @renderable(show: true) }
Easy to fetch:
query($id: ID!) { post(id: $id) { id # ... other fields imageThumbUrl } }
How do I get the responsive image data in a query?
Add a conversions
field with responsive: true
.
type Post { image: LaravelMediaLibraryData @migrationSkip @eagerLoad(name: "media") @laravelMediaLibraryData( collection: "image" conversions: [{ name: "thumb", responsive: true}] ) }
TODO: explain how to get data
How do I eager load?
Add an @eagerLoad
directive to eager load the table and avoid a N+1 problem.
type Post { image: LaravelMediaLibraryData @migrationSkip @eagerLoad(name: "media") @laravelMediaLibraryData( collection: "image" fields: ["url", "description"] ) }
Sponsors
We want to thanks to Spatie for its wonderful Spatie's Laravel Media Library.
Contributing
Any contributions are welcome. Please send a PR.