spatie / laravel-medialibrary

Associate files with Eloquent models
https://spatie.be/docs/laravel-medialibrary
MIT License
5.78k stars 1.08k forks source link

Add and use user_id column in media table? #75

Closed dambridge closed 9 years ago

dambridge commented 9 years ago

I'm thinking this is possible through the custom model capabilities, but I confess I'm not sure to go about that exactly. For instance, where would I pass an additional value when uploading a file toMediaLibrary()?

freekmurze commented 9 years ago

If you just want to add a custom property to an uploaded media file it's best to use custom properties.

If you really want to use your own model, the documentation provides some starting points on how to do that.

dbpolito commented 9 years ago

FYI, in case someone else needs it, i did this using my own model with this:

<?php

namespace App;

use Spatie\MediaLibrary\Media as BaseMedia;

class Media extends BaseMedia
{
    /**
     * Boot events
     * @return void
     */
    public static function boot()
    {
        parent::boot();

        static::creating(function ($media) {
            if ($user = auth()->getUser()) {
                $media->user_id = $user->id;
            }
        });
    }

    /**
     * User relationship (one-to-one)
     * @return App\User
     */
    public function user()
    {
        return $this->belongsTo('App\User');
    }
}

You also have to create a migration to add column user_id.

dambridge commented 9 years ago

Ah, great idea @dbpolito !

dambridge commented 9 years ago

I guess now my challenge is figuring out how to do something like getMedia($request->collection)->with('user')

dambridge commented 9 years ago

Never mind, sorted, and thanks again for that. Sorry to necro-bump this issue!

freekmurze commented 9 years ago

naumanahmed19 commented 7 years ago

@freekmurze can you please update docs for this the solution @dbpolito suggested seems not working for "^5.0.0"

freekmurze commented 7 years ago

https://docs.spatie.be/laravel-medialibrary/v5/advanced-usage/using-your-own-model

farbodakvan commented 1 year ago

FYI, in case someone else needs it, i did this using my own model with this:

<?php

namespace App;

use Spatie\MediaLibrary\Media as BaseMedia;

class Media extends BaseMedia
{
    /**
     * Boot events
     * @return void
     */
    public static function boot()
    {
        parent::boot();

        static::creating(function ($media) {
            if ($user = auth()->getUser()) {
                $media->user_id = $user->id;
            }
        });
    }

    /**
     * User relationship (one-to-one)
     * @return App\User
     */
    public function user()
    {
        return $this->belongsTo('App\User');
    }
}

You also have to create a migration to add column user_id.

Thank you for the very good way There is a change for Laravel 9, I said to share

public static function boot()
{
    parent::boot();

    static::creating(function ($media) {
        if ($user = Auth::user()) {
            $media->user_id = $user->id;
        }
    });
}

public function user()
{
    return $this->belongsTo(User::class);
}
LiamKarlMitchell commented 1 year ago

For Laravel 9, I wanted to store authenticated user against the Media, but also customize the searching of existing media using Advanced Nova Media Library

Additionally A user should only be able to remove the media if they are Admin or Authenticated as the owner, but thats an implementation detail so I'll leave it out here. But to quote from this question

This package doesn't handle anything regarding permissions, that's something you should handle yourself in your app.

To go further on that I may need to make a Policy or Gate? for Media.

Model

sail artisan make:model Media
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Spatie\MediaLibrary\MediaCollections\Models\Media as BaseMedia;

class Media extends BaseMedia
{
    /**
     * All the relationships to be touched.
     *
     * @var array
     */
    protected $touches = ['model'];

    /**
     * Boot events
     * @return void
     */
    public static function boot()
    {
        parent::boot();

        static::creating(function ($media) {
            $user = auth()->getUser();
            if ($user) {
                $media->user_id = $user->id;
            }
        });
    }

    /**
     * User that uploaded the image.
     *
     * @return BelongsTo
     */
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }
}

Migration:

sail artisan make:migration add_user_to_media_table
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('media', function (Blueprint $table) {
            $table->foreignId('user_id')->index()->nullable()->constrained()->comment('The user that owns this record');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('media', function (Blueprint $table) {
            $table->dropConstrainedForeignId('user_id');
        });
    }
};
sail artisan migrate

Extending Advanced Nova Media Library Media Controller to query existing media based on User that uploaded the media.

Make a controller as needed.

App\Http\Controllers\AdvancedNovaMediaLibrary\MediaController.php

<?php

namespace App\Http\Controllers\AdvancedNovaMediaLibrary;

use App\Http\Controllers\Controller;
use Ebess\AdvancedNovaMediaLibrary\Http\Requests\MediaRequest;
use Ebess\AdvancedNovaMediaLibrary\Http\Resources\MediaResource;
use Exception;
use Auth;

class MediaController extends Controller
{
    public function index(MediaRequest $request)
    {
        if (!config('nova-media-library.enable-existing-media')) {
            throw new Exception('You need to enable the `existing media` feature via config.');
        }

        $hideCollections = config('nova-media-library.hide-media-collections', []);
        $mediaClass = config('media-library.media_model');
        $mediaClassIsSearchable = method_exists($mediaClass, 'search');

        $searchText = $request->input('search_text') ?: null;
        $perPage = $request->input('per_page') ?: 15;

        $query = null;

        if ($searchText && $mediaClassIsSearchable) {
            $query = $mediaClass::search($searchText);
        } else {
            $query = $mediaClass::query();

            if ($searchText) {
                $query->where(function ($query) use ($searchText) {
                    $query->where('name', 'LIKE', '%' . $searchText . '%');
                    $query->orWhere('file_name', 'LIKE', '%' . $searchText . '%');
                });
            }

            $query->latest();
        }

        if (!empty($hideCollections)) {
            if (!is_array($hideCollections)) {
                $hideCollections = [ $hideCollections ];
            }

            $query->whereNotIn('collection_name', $hideCollections);
        }

        // Custom code: Filter by uploaded user.
        $user = Auth::getUser(); //$request->getUser();
        if ($user) {
            $query->whereUserId($user->id);
        }
        // End of custom code.

        $results = $query->paginate($perPage);

        return MediaResource::collection($results);
    }
}

Make a route accordingly to your controller? Well this one was tricky as I could not seem to over-ride the routes made from vendor package due to load order like below.

~web.php~

Route::get('/media', [AdvancedNovaMediaLibrary\MediaController::class, 'index']);

But I could get composer to load my own file by overloading vendor classes. https://downing.tech/posts/overriding-vendor-classes

I created an Overrides/Ebess/AdvancedNovaMediaLibrary/AdvancedNovaMediaLibraryServiceProvider.php

<?php

namespace Ebess\AdvancedNovaMediaLibrary;

use Illuminate\Support\Facades\Route;
use Laravel\Nova\Nova;
use Laravel\Nova\Events\ServingNova;
use Illuminate\Support\ServiceProvider;

class AdvancedNovaMediaLibraryServiceProvider extends ServiceProvider
{
    /***
     * @var string Path to vendor files will be figured out in constructor.
     */
    private $vendorpath = '';

    /**
     * Create a new service provider instance.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @return void
     */
    public function __construct($app)
    {
        $this->app = $app;
        $this->vendorpath = base_path('vendor/ebess/advanced-nova-media-library');
    }
    public function boot()
    {
        $this->publishes([
            $this->vendorpath . '/config/nova-media-library.php' => config_path('nova-media-library.php'),
        ], 'nova-media-library');

        $this->app->booted(function () {
            $this->routes();
        });

        Nova::serving(function (ServingNova $event) {
            Nova::script('media-lib-images-field', $this->vendorpath.'/dist/js/field.js');
        });
    }

    protected function routes()
    {
        if ($this->app->routesAreCached()) {
            return;
        }

        Route::middleware(['nova'])
            ->prefix('nova-vendor/ebess/advanced-nova-media-library')
            ->group(base_path('routes/novamedia.php'));
    }
}

Then I update my composer.json to not load the vendor file and load mine instead.

    "autoload": {
        "exclude-from-classmap": [
            "vendor/ebess/advanced-nova-media-library/src/AdvancedNovaMediaLibraryServiceProvider.php"
        ],
        "psr-4": {
            //...
            "Ebess\\AdvancedNovaMediaLibrary\\": "app/Overrides/Ebess/AdvancedNovaMediaLibrary"
        }
    },

Then I run

sail composer dump-autoload

Looking back I probably could have just done the same but for the Controller (not sure).

And of course update the model in config/media-library.php to point to your custom one.

    /*
     * The fully qualified class name of the media model.
     */
    'media_model' => Media::class

And with all that, I was able to have the Nova Media Library filter existing images by the user that uploaded the image. Would also be nice to filter by the collection or type of model the image is being uploaded for...

RejownAhmed commented 4 months ago

FYI, in case someone else needs it, i did this using my own model with this:

<?php

namespace App;

use Spatie\MediaLibrary\Media as BaseMedia;

class Media extends BaseMedia
{
    /**
     * Boot events
     * @return void
     */
    public static function boot()
    {
        parent::boot();

        static::creating(function ($media) {
            if ($user = auth()->getUser()) {
                $media->user_id = $user->id;
            }
        });
    }

    /**
     * User relationship (one-to-one)
     * @return App\User
     */
    public function user()
    {
        return $this->belongsTo('App\User');
    }
}

You also have to create a migration to add column user_id.

Inside a Laravel queued job this configuration won't work. So it'd be better to use custom properties, I believe