Skip to content

Commit

Permalink
[0.2] UploadableImage trait for Spatie MediaLibrary (#32)
Browse files Browse the repository at this point in the history
* UploadableImage trait for Spatie MediaLibrary
* Update README.md for UploadableImage
  • Loading branch information
shaoshiva authored Feb 14, 2018
1 parent 91929b8 commit f3efe5f
Show file tree
Hide file tree
Showing 3 changed files with 186 additions and 69 deletions.
116 changes: 65 additions & 51 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,27 +240,63 @@ public function messages()

### Image Field : `UploadableImage` Trait

If you use Image CRUD Field, you can implement this Trait on your Model to automatically upload / delete image(s) on server.
If you use the [Image CRUD Field](https://laravel-backpack.readme.io/docs/crud-fields#section-image), you can implement this trait on your model to automatically manage saving and deleting the image on the server.

Example:
```php
// Article Model
namespace App\Models;

class Article extends \Backpack\NewsCRUD\app\Models\Article
use Backpack\CRUD\CrudTrait;
use Illuminate\Database\Eloquent\Model;
use Novius\Backpack\CRUD\ModelTraits\UploadableImage;

class Example extends Model
{
use Sluggable, SluggableScopeHelpers;
use HasTranslations;
use CrudTrait;
use UploadableImage;

protected $fillable = ['slug', 'title', 'content', 'image', 'status', 'category_id', 'featured', 'date', 'thumbnail'];
protected $translatable = ['slug', 'title', 'content'];
protected $fillable = ['title', 'image', 'thumbnail'];

public function uploadableImages()
{
return [
[
'name' => 'image', // The attribute name where to store the image path
'slug' => 'title', // The attribute name from which to generate the image file name (optionnal)
],
[
'name' => 'thumbnail',
],
];
}
}
```

If you want to perform some custom actions on your image after saving or deleting it :

```php
namespace App\Models;

use Backpack\CRUD\CrudTrait;
use Illuminate\Database\Eloquent\Model;
use Novius\Backpack\CRUD\ModelTraits\UploadableImage;

class Example extends Model
{
use CrudTrait;
use UploadableImage {
imagePathSaved as imagePathSavedNative;
imagePathDeleted as imagePathDeletedNative;
}

protected $fillable = ['title', 'image', 'thumbnail'];

public function uploadableImages()
{
return [
[
'name' => 'image', // Attribute name where to stock image path
'slug' => 'title', // Attribute name to generate image file name (optionnal)
'name' => 'image', // The attribute name where to store the image path
'slug' => 'title', // The attribute name from which to generate the image file name (optionnal)
],
[
'name' => 'thumbnail',
Expand All @@ -269,70 +305,48 @@ class Article extends \Backpack\NewsCRUD\app\Models\Article
}

/**
* You might like to perform some custom actions on your image after saving it.
* Callback triggered after image saved on disk
*/
public function imagePathSaved(string $imagePath, string $imageAttributeName = null, string $diskName = null)
{
//perfoms some custom actions here
$this->addMedia($imagePath)
->preservingOriginal()
->toMediaCollection();
if (!$this->imagePathSavedNative()) {
return false;
}

// Do what you want here

return true;
}

/**
* You might like to perform some custom actions after deleting the image.
* Callback triggered after image deleted on disk
*/
public function imagePathDeleted(string $imagePath, string $imageAttributeName = null, string $diskName = null)
{
$this->clearMediaCollection();
if (!$this->imagePathDeletedNative()) {
return false;
}

// Do what you want here

return true;
}
}
```

#### MediaLibrary

```
If you want to store the images in the [MediaLibrary](https://github.com/spatie/laravel-medialibrary) provided by Spatie, use the trait `SpatieMediaLibrary\UploadableImage` instead of `UploadableImage`.

If you like to use imagePathSaved and the medialibrary of Spatie, you will need :
For example with the MediaLibrary you can easily manage conversions (crop, resize, ...).

1. Override this method and adds whatever actions you prefer.
2. The configuration file _medialibrary.php_ should define an existing file system and image driver:
```
'defaultFilesystem' => 'public',
'image_driver' => 'imagick',
```
3. Your _composer.json_ should include:
```
"spatie/laravel-medialibrary": "your.version.here"
```
___

#### Translations

```php
// ArticleCrudController
Both traits `UploadableImage` and `SpatieMediaLibrary\UploadableImage` are compatible with the [translation package](https://github.com/spatie/laravel-translatable) provided by Spatie.

$this->crud->addField([
'label' => 'Image',
'name' => 'image',
'type' => 'image',
'upload' => true,
'crop' => true, // set to true to allow cropping, false to disable
'aspect_ratio' => 0, // ommit or set to 0 to allow any aspect ratio
'prefix' => '/storage/',
]);
In the case of translatable images with `SpatieMediaLibrary\UploadableImage`, the name of the collection where the image is stored is composed of the name of the attribute and the locale, separated by a dash (eg. `image-en`, `image-fr`, ...).

$this->crud->addField([
'label' => 'Image',
'name' => 'thumbnail',
'type' => 'image',
'upload' => true,
'crop' => true, // set to true to allow cropping, false to disable
'aspect_ratio' => 0, // ommit or set to 0 to allow any aspect ratio
'prefix' => '/storage/',
]);
```
___


### CRUD : custom routes
Expand Down
75 changes: 75 additions & 0 deletions src/ModelTraits/SpatieMediaLibrary/UploadableImage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

namespace Novius\Backpack\CRUD\ModelTraits\SpatieMediaLibrary;

use Novius\Backpack\CRUD\ModelTraits\UploadableImage as UploadableImageOriginal;

/**
* Trait UploadableImage
*
* To make media upload work :
* - implement the trait Spatie\MediaLibrary\HasMedia\HasMediaTrait on your model
* - call $this->setUploadedImage($value) in your model attribute mutator
*
* @package Novius\Backpack\CRUD\ModelTraits\SpatieMediaLibrary
*/
trait UploadableImage
{
use UploadableImageOriginal {
imagePathSaved as imagePathSavedOriginal;
imagePathDeleted as imagePathDeletedOriginal;
}

/**
* Callback triggered after image saved on disk
*
* @param string $imageAttributeName
* @param string|null $imagePath
* @param string|null $diskName
* @return bool
*/
public function imagePathSaved(string $imagePath, string $imageAttributeName = null, string $diskName = null) : bool
{
// Adds the image to the medialibrary
$this->addMedia($imagePath)
->preservingOriginal()
->toMediaCollection($this->getImageCollectionName($imageAttributeName));

return $this->imagePathSavedOriginal($imagePath, $imageAttributeName, $diskName);
}

/**
* Callback triggered after image deleted on disk
*
* @param string $imagePath
* @param string|null $imageAttributeName
* @param string|null $diskName
* @return bool
*/
public function imagePathDeleted(string $imagePath, string $imageAttributeName = null, string $diskName = null) : bool
{
// Removes the image from the medialibrary
$this->clearMediaCollection($this->getImageCollectionName($imageAttributeName));

return $this->imagePathDeletedOriginal($imagePath, $imageAttributeName, $diskName);
}

/**
* Gets the localized image attribute name
*
* @param string $imageAttributeName
* @param string|null $locale
* @return string
*/
public function getImageCollectionName(string $imageAttributeName, string $locale = null)
{
$collectionName = $imageAttributeName;

// Appends the locale if translatable
if ($this->isTranslatableImageAttribute($imageAttributeName)) {
$collectionName .= '-'.($locale ?? $this->getLocale());
}

return $collectionName;
}
}
64 changes: 46 additions & 18 deletions src/ModelTraits/UploadableImage.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,31 +35,22 @@ public static function bootUploadableImage()
*/
public function fillUploadedImageAttributeValue(string $imageAttributeName, string $path)
{
if (method_exists($this, 'isTranslatableAttribute')
&& $this->isTranslatableAttribute($imageAttributeName)
) {
$this->setTranslation($imageAttributeName, (string) request('locale', $this->getLocale()), $path); // Default value is relevant when using seeders or any environment where we dont have acces to "request".
// Generates a unique URI path (for cache bursting)
$uniquePath = $this->generateImageUniqueUriPath($path);

if (!empty($path)) {
$path = preg_replace('/\?v=.*/', '', $path);
$this->setTranslation($imageAttributeName, (string) request('locale', $this->getLocale()), $path.'?v='.uniqid());
}
if ($this->isTranslatableImageAttribute($imageAttributeName)) {
$this->setTranslation($imageAttributeName, $this->getLocale(), $uniquePath);
} else {
$this->{$imageAttributeName} = $path;

if (!empty($path)) {
$path = preg_replace('/\?v=.*/', '', $path);
$this->{$imageAttributeName} = $path.'?v='.uniqid();
}
$this->{$imageAttributeName} = $uniquePath;
}
}

/**
* Called after image saved on disk
* Callback triggered after image saved on disk
*
* @param string $imageAttributeName
* @param string $imagePath
* @param string $diskName
* @param string|null $imagePath
* @param string|null $diskName
* @return bool
*/
public function imagePathSaved(string $imagePath, string $imageAttributeName = null, string $diskName = null) : bool
Expand All @@ -68,7 +59,7 @@ public function imagePathSaved(string $imagePath, string $imageAttributeName = n
}

/**
* Called after image deleted on disk
* Callback triggered after image deleted on disk
*
* @param string $imagePath
* @param string|null $imageAttributeName
Expand All @@ -80,6 +71,43 @@ public function imagePathDeleted(string $imagePath, string $imageAttributeName =
return true;
}

/**
* Generates a unique image URI path
*
* @param string $path
* @return string
*/
public function generateImageUniqueUriPath(string $path)
{
$path = preg_replace('/\?v=.*/i', '', $path);
if (!empty($path)) {
$path .= '?v='.uniqid();
}

return $path;
}

/**
* Checks if the given image attribute name is translatable
*
* @param string $imageAttributeName
* @return bool
*/
public function isTranslatableImageAttribute(string $imageAttributeName)
{
return $this->canTranslateImage() && $this->isTranslatableAttribute($imageAttributeName);
}

/**
* Checks if the model can translate an image
*
* @return bool
*/
public function canTranslateImage()
{
return method_exists($this, 'isTranslatableAttribute') && method_exists($this, 'setTranslation');
}

/**
* Get model attributes name for image upload
* Simple example: return ['name' => 'image', slug' => 'title'];
Expand Down

0 comments on commit f3efe5f

Please sign in to comment.