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

Add spatie media library to demo #456

Merged
merged 23 commits into from
Jun 28, 2023
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
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
81 changes: 81 additions & 0 deletions app/Http/Controllers/Admin/ProductCrudController.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class ProductCrudController extends CrudController
use \Backpack\CRUD\app\Http\Controllers\Operations\BulkDeleteOperation;
use \Backpack\CRUD\app\Http\Controllers\Operations\BulkCloneOperation;
use \Backpack\CRUD\app\Http\Controllers\Operations\InlineCreateOperation;
use \Backpack\Pro\Http\Controllers\Operations\DropzoneOperation { dropzoneUpload as traitDropzoneUpload; }

public function setup()
{
Expand Down Expand Up @@ -206,11 +207,91 @@ protected function setupCreateOperation()
],
]);

CRUD::field('main_image')
->label('Main Image')
->type('image')
->tab('Media')
->wrapper(['class' => 'form-group col-md-4'])
->withMedia();

CRUD::field('privacy_policy')
->label('Privacy policy document')
->type('upload')
->tab('Media')
->wrapper(['class' => 'form-group col-md-4'])
->withMedia();

CRUD::field('specifications')
->label('Specifications')
->type('upload_multiple')
->tab('Media')
->wrapper(['class' => 'form-group col-md-4'])
->withMedia();

CRUD::field('gallery')
->type('repeatable')
->tab('Media')
->subfields([
[
'name' => 'image_title',
'type' => 'text',
'wrapper' => [
'class' => 'form-group col-md-6',
],
],
[
'name' => 'gallery_image',
'label' => 'image',
'type' => 'image',
'wrapper' => [
'class' => 'form-group col-md-6',
],
'withMedia' => true,
],

[
'name' => 'gallery_image_drm',
'label' => 'Image DRM',
'type' => 'upload',
'wrapper' => [
'class' => 'form-group col-md-6',
],
'withMedia' => true,
],
[
'name' => 'gallery_image_specifications',
'label' => 'Image Specifications',
'type' => 'upload_multiple',
'wrapper' => [
'class' => 'form-group col-md-6',
],
'withMedia' => true,
],
[
'name' => 'gallery_image_certificates',
'label' => 'Image Certificates',
'type' => 'dropzone',
'wrapper' => [
'class' => 'form-group col-md-6',
],
'withMedia' => true,
],
]);

$this->crud->setOperationSetting('contentClass', 'col-md-12');
}

protected function setupUpdateOperation()
{
$this->setupCreateOperation();
}

public function dropzoneUpload()
{
if (app('env') === 'production') {
return response()->json([]);
}

return $this->traitDropzoneUpload();
}
Comment on lines +295 to +302
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same with dropzone. Can't we bind on the DropzoneOperation trait, and prevent all dropzone uploads in production? It's too broad in a normal app, but in our case that's what we want, to prevent all uploads.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not urgent - just a thought. Right now I just want to merge this ASAP.

Copy link
Contributor Author

@pxpm pxpm Jun 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you thinking something like this:

<?php

namespace App\Http\Controllers\Operations;

if (! env('APP_ENV') === 'production') {
    trait DropzoneOperation
    {
        public function dropzoneUpload()
        {
            return response()->json([]);
        }
    }
} else {
    trait DropzoneOperation
    {
        use \Backpack\Pro\Http\Controllers\Operations\DropzoneOperation;
    }
}

And then in demo we use App\Http\Controllers\Operations\DropzoneOperation instead of the one in the package ?

If you agree I can push this change.

Cheers

}
70 changes: 68 additions & 2 deletions app/Models/Product.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@
use App\Enums\ProductStatus;
use Backpack\CRUD\app\Models\Traits\CrudTrait;
use Backpack\CRUD\app\Models\Traits\SpatieTranslatable\HasTranslations;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;

class Product extends Model
class Product extends Model implements HasMedia
{
use CrudTrait;
use HasTranslations;
use HasFactory;
use InteractsWithMedia;

/*
|--------------------------------------------------------------------------
Expand All @@ -21,18 +25,80 @@ class Product extends Model
*/

protected $table = 'products';

protected $primaryKey = 'id';

public $timestamps = true;

// protected $guarded = ['id'];
protected $fillable = ['name', 'description', 'details', 'features', 'price', 'category_id', 'extras', 'status', 'condition'];
protected $fillable = ['name', 'description', 'details', 'features', 'price', 'category_id', 'extras', 'status', 'condition', 'gallery', 'main_image', 'privacy_policy', 'specifications'];

// protected $hidden = [];
public $translatable = ['name', 'description', 'details', 'features', 'extras'];

public $casts = [
'features' => 'object',
'extra_features' => 'object',
'status' => ProductStatus::class,
'gallery' => 'json',
'specifications' => 'array',
];

public function mainImage(): Attribute
{
return Attribute::make(
set: function ($item) {
if (app('env') === 'production') {
return null;
}

return $item;
},
);
}

public function privacyPolicy(): Attribute
{
return Attribute::make(
set: function ($item) {
if (app('env') === 'production') {
return null;
}

return $item;
},
);
}

public function specifications(): Attribute
{
return Attribute::make(
set: function ($item) {
if (app('env') === 'production') {
return null;
}

return json_encode($item);
},
);
}
Comment on lines +73 to +84
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering... if the actual upload now... is being done with Uploaders, not Mutators... why are we using Mutators to catch and stop the upload process?

Why don't we bind to all our uploaders... and prevent the upload process from happening... for ALL uploaders, if the env is production? Wouldn't that be cleaner, and more secure? That way nobody can introduce a vulnerability in our demo, all Uploaders are prevented from working in production.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or maybe we can catch the ->withMedia() and ->withFiles() helper and do that there?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't urgent though. Just a thought. What I want right now is to get this merged and out of the way, ASAP.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can catch the withMedia and withFiles, with that we prevent the uploader events from beeing registered, so we wouldn't even need to bind into the uploader classes as they wouldn't be registered/called.

That wouldn't prevent much, just the upload from happening, but anything you submit in the input would be saved to the database, except it didn't go through the upload process.

At the moment we can't bind to our uploaders as they aren't resolved from the container, they are statically initialized.
We can work that out, or use the mutator as we are doing now. Everything is "working", except we always send empty values to the uploaders 🤷

Probably it's like you said, it's more future proof, but at the moment it's not a requirement.


public function gallery(): Attribute
{
return Attribute::make(
set: function ($item) {
if (app('env') === 'production') {
array_walk($item, function (&$row) {
unset($row['gallery_image'], $row['gallery_image_drm'], $row['gallery_image_specifications'], $row['gallery_image_certificates']);

return $row;
});
}

return json_encode($item);
},
);
pxpm marked this conversation as resolved.
Show resolved Hide resolved
}
/*
|--------------------------------------------------------------------------
| FUNCTIONS
Expand Down
8 changes: 7 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@
"laravel/legacy-factories": "^1.0",
"mews/purifier": "^3.3",
"backpack/backupmanager": "^4.0",
"backpack/editable-columns": "^2.0"
"backpack/editable-columns": "^2.0",
"spatie/laravel-medialibrary": "^10.7",
"backpack/medialibrary-uploaders": "dev-main"
},
"require-dev": {
"fakerphp/faker": "~1.4",
Expand All @@ -55,6 +57,10 @@
"0": {
"type": "composer",
"url": "https://repo.backpackforlaravel.com/"
},
"backpack/medialibrary-uploaders": {
"type": "vcs",
"url": "https://github.com/Laravel-Backpack/medialibrary-uploaders/"
}
},
"autoload": {
Expand Down
Loading