Getting Started with Spatie
Tags: laravel, php, database, beginners
Published: 2023-09-17 04:00:37

I was working on a feature where a user chooses an image, views the thumbnail, and saves it if it's acceptable. During editing, a user can view the stored image, select another one, see its thumbnail, and save it.
This task required the use of Livewire and Spatie. I successfully implemented the creation part, but I encountered challenges during the editing phase. Fortunately, my senior developer came to the rescue and solved the problem. I studied his code, which not only helped me grasp the fundamentals but also initiated my deeper exploration of Spatie for a comprehensive understanding.

While delving into Spatie, a package designed for comprehensive media management, I discovered that there was much to explore. This article marks the beginning of a series on this topic, with more articles to follow in the future.

In today's article, I will discuss: 
1. Spatie documentation
2. How I have come to understand it

Laravel 10
Spatie v10

**Spatie/Laravel-medialibrary**

This package is designed to work seamlessly with Eloquent models. In Laravel, Eloquent serves as an Object-Relational Mapper (ORM) that interacts with databases.

According to the documentation:
[Spatie official documentation](https://spatie.be/docs/laravel-medialibrary/v10/introduction)

> You can associate a file with a model like this:
> $yourModel = YourModel::find(1);
> $yourModel
>    ->addMedia($pathToFile)
>    ->toMediaCollection();

> The file will now be associated with the YourModel instance and will be moved to the disk you've configured.
> If you want to not move, but copy, the original file you can call preservingOriginal:
> $yourModel
>    ->addMedia($pathToFile)
>    ->preservingOriginal()
>    ->toMediaCollection();

> To retrieve files you can use the getMedia-method:
> $mediaItems = $yourModel->getMedia();
> Since retrieving the first media and the URL for the first media for an object is such a common scenario, the getFirstMedia and getFirstMediaUrl convenience-methods are also provided:
> $media = $yourModel->getFirstMedia();
> $url = $yourModel->getFirstMediaUrl();

That's about it.

I genuinely appreciate its concise explanation, but as a beginner, I needed to read it multiple times, watch videos, and conduct additional research to fully comprehend it. Let's break this down.

When dealing with images or files, the objective is to store both the data and their corresponding URLs or paths in our database. Thus, I understand the sentence 'the model needs to implement the following interface and trait:'

```
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;

class YourModel extends Model implements HasMedia
{
    use InteractsWithMedia;
}
```

Spatie provides an efficient way to manage and associate media files (images, videos, documents, etc.) by handling media files and storing their metadata.

Next, we want to handle the actual image data. 
I'll follow the official tutorial. Let's say we have an image in a storage folder. (I'll talk about how a user can freely upload an image in the next article.)
`storage/app/example.jpg` or `storage_path('app/example.jpg')`

So, the following code associates the image with a Model instance:

```
$yourModel = YourModel::find(1);
$yourModel->addMedia(storage_path('app/example.jpg'));
```

When you install 'composer require "spatie/laravel-medialibrary:^10.0.0,"' a `media-library.php` file is created in the config folder. Inside this file, you'll find the following configuration:

```
/*
 * The disk on which to store added files and derived images by default. Choose
 * one or more of the disks you've configured in config/filesystems.php.
 */
    'disk_name' => env('MEDIA_DISK', 'public'),
...
```

This is the default configuration. If you want to customize it further, please refer to the documentation.

```
$yourModel = YourModel::find(1);
$yourModel
   ->addMedia($pathToFile)
   ->toMediaCollection(); 
```

To explain what `->toMediaCollection();` does: it stores the image in the designated folder, which in this case is `public/media`.

_If you want to not move, but copy, the original file you can call preservingOriginal:_

```
$yourModel
   ->addMedia($pathToFile)
   ->preservingOriginal()
   ->toMediaCollection();
```

By using `preservingOriginal()`, the image will be stored in the public folder, and the original file specified in 'addMedia' will be retained in its original location.

Before moving on to the retrieval part, I'd like to address some questions I had:

1. How is the data stored in the database?
2. Is it stored as an object or an array?
3. How can we determine which image is associated with which data set?

Initially, I assumed that a new column for the image should be created. For instance, in a blog application, where each blog article typically has an id, a title, an article body, and an image, I would include the image in the same table and column. However, in Spatie, the media collection is managed internally by the package, and it creates a separate table (usually named 'media') to store information.

Regarding the storage format, the data about media files and their associations is stored in _a structured database table_, not as PHP objects or arrays.

Here's a screenshot:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xfipce6g9zzc78dl25ld.jpeg)

`toMediaCollection('images');` 
'images' here is a collection name, which can be any name you choose. If not specified, it defaults to 'default.'

Now, consider a scenario where you have multiple blog articles, each associated with an image stored in a separate table. How can you determine which image belongs to which article? 
 (The below is not a code but just an idea. You get the point.)

blog
id => 1,
title => title1,
article => article1

id => 2,
title => title2,
article => article2

image
id => 1,
image => image1,

id => 2,
image => image2 

Spatie uses a unique ID for media items, distinct from the Model's ID. This allows you to associate a specific image with a specific blog article without relying on matching IDs:

```
$blog = Blog::find(1); 
$media = $blog->addMedia($pathToFile)
              ->toMediaCollection('images');
```

`$media` will contain information about the media item, including its unique ID.

**Retrieving data**
To retrieve data, I should keep in mind that it's stored in the database, and we'll be handling it as collections.

To retrieve files you can use the getMedia-method as follows:

To retrieve files from the default collection:
```
$mediaItems = $yourModel->getMedia();
```

To retrieve files from all collections, you can use the getMedia method with an asterisk:
```
$mediaItems = $yourModel->getMedia("*");
```

To retrieve files from a specific collection, use the collection name, e.g., 'images':
```
$yourModel->getMedia('images'); 
```

We can retrieve the first media item, typically the only one, from the 'images' collection associated with a specified item, such as a blog article, using the getFirstMedia() method:
```
$blog = Blog::find(1);
$image = $blog->getFirstMedia('images');
```

This method returns a collection of Media objects.
To retrieve the URL for the first media item, you can use the getFirstMediaUrl() method:
```
$media = $yourModel->getFirstMedia();
$url = $yourModel->getFirstMediaUrl();
```

There are additional methods for retrieving URLs or paths. Here are some examples used in the official tutorial video, demonstrating how to retrieve the URL and path for the latest created data (`last()`) in the 'images' collection. If you attempt to get data from a 'non-existing-collection,' an empty array is returned:

```
Article::last()->getFirstMedia('images')->getUrl();
Article::last()->getFirstMedia('images')->getPath();
Article::last()->getMedia('non-existing-collection);
=> all:[]
```

**Removing data**
You can remove data by calling the delete method on an instance of Media:

```
$mediaItems[0]->delete();
```

One last thing. When defining a model collection, we can do so in a file in a Models folder, using a function, `registerMediaCollections()`.

In your model,

```
public function registerMediaCollections(): void
{
    $this->addMediaCollection('my-collection')
        //add options
        ...

    // you can define as many collections as needed
    $this->addMediaCollection('my-other-collection')
        //add options
        ...
}
```

Examining the actual code within the newly generated files and folders and reading the associated descriptions provided there and the database table structure, greatly contributed to my understanding of how Spatie functions. Additionally, grasping how data is interconnected through behind-the-scenes handling of additional IDs was enlightening. 
Now, the next step is to apply this package in a practical context.