corollarium/modelarium-medialibrary

Integrates Spatie Laravel-media-library with Modelarium and GraphQL

v0.5.0 2021-01-26 20:14 UTC

This package is auto-updated.

Last update: 2024-04-05 19:31:17 UTC


README

Latest Stable Version Total Downloads License

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

Corollarium

We want to thanks to Spatie for its wonderful Spatie's Laravel Media Library.

Contributing contributions welcome

Any contributions are welcome. Please send a PR.