Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[0.2] UploadableImage trait for Spatie MediaLibrary #32

Merged
merged 3 commits into from
Feb 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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