spatie/laravel-medialibrary change primary key - php

I'm using the package spatie/laravel-medialibrary and I want to change the primaryKey on their modal called Media, without editing the package src file.
In my project, I'm using uuids as primary keys for all my models, so naturally, I want to do the same thing for the Media.php model offered by this package.
I already changed the migration to reflect that, by removing the line $table->bigInteger('id') and changing the line $table->uuid('uuid')->nullable(); to table->uuid('uuid')->unique()->primary();
However, now I also want to let the model know I'm using a different key, by setting up protected $primaryKey = 'uuid'; and protected $keyType = 'string'; but I can't find a way to do this outside the packages src file for the Media.php model
Basically, what I want to end up doing is just implementing the HasMedia interface and using the InteractsWithMedia trait on my Profile model, like this:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
class Profile extends Model implements HasMedia
{
use InteractsWithMedia;
}
Any suggestions on how to achieve this?
Thanks.

Spatie's medialibrary package gives you the option to use your own media model, as described in their docs.
Just create your custom model and extend the library's Media model. You can then modify that csutom model to fit your needs.
use Spatie\MediaLibrary\MediaCollections\Models\Media as BaseMedia;
class Media extends BaseMedia
{
protected $primaryKey = 'uuid';
protected $keyType = 'string';
public $incrementing = false;
// ...
}
Remember to set the media_model key in config/media-library.php to your model's FQCN.
'media_model' => App\YourMediaModel::class,

Related

Laravel How to reference model outside of package?

I'm using laravel 8 with a custom package. I have models in this package and outside this package, for example User (from default auth) and thread model (in a package). I've got thread-user relationship belongsToMany for "like"-system that I need to have working. here is a piece of code, of thread model.
namespace LaraChan\Core\Models;
use Illuminate\Database\Eloquent\Model;
use LaraChan\Core\Traits\Renderable;
use LaraChan\Core\Traits\Uuids;
use LaraChan\Core\Traits\Image;
use App\Models\User;
class Thread extends Model
{
use Uuids, Image, Renderable;
protected $fillable = [
'id',
'board',
'title',
'image',
'body',
'user_author'
];
public function userlikes(){
return $this->belongsToMany(User::class, 'threads_liked', 'thread_id', 'user_id');
}
}
it does not seem to me that relationship is working. I've used Larachan package. https://github.com/anthonybudd/LaraChan
In order to reference a model outside of a package in Laravel, you can use the full namespace of the model.
For example, if your model is located in the App\Models namespace, you can reference it like this:
$model = App\Models\MyModel::find(1);

Laravel on some kind of Model Ready method

Well i don't know how to format the title of this post in very clear way, but here's my question:
Say i have
Posts::find('1);
Photos:find('1');
... and so on, every mode db request
now by default i can access db columns, for instance the id: through model->id
$Photos = Photos::find('1')->first();
echo $Photos->id; // will return 1
what i want is that i need all those kind of requests to add a custom field automatically like hashed_id, which is not in the database, which in return will make all models have a hashed_id as well, i know i can add that field to database and then grab it but i need it for different reasons/implementations
i did create a BaseModel and every Model will extend that BaseModel, so Photos extends BaseModel, BaseModel extends Model... and all that etc etc.
but i need some kind of constructor, upon retrieving data to process the data automatically without having to add -let's say- a hash_id() after retrieving the data.
something like, onAfterGet(), onReady()....sort of commands.
i hope my question is clear.
Thanks.
What you're looking for is an Accessor. Accesors can be used to add custom attributes to the model. Combine this with the $appends property and you have exactly what you need. The $appends property adds the custom accessor in every result.
You can do this by creating a base model like you've stated in the question or by using traits. I'll show you an example on how to achieve this using a base model.
Let's create base model called BaseModel. All other models that need this custom attribute will extend this.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class BaseModel extends Model
{
protected $appends = ['hashed_id'];
public function getHashedIdAttribute()
{
return some_hash_function($this->id);
}
}
We have a Image model which extends our BaseModel.
<?php
namespace App;
class Image extends BaseModel
{
}
Now every result from the Image model will have the hashed_id field added by default.
Accesor documenation https://laravel.com/docs/5.4/eloquent-mutators#defining-an-accessor
If I understand you right, all you need to do is to define mutator, for example:
<?php
class Photo extends Model
{
/* ... model implementation ... */
public function getHashedIdAttribute()
{
return md5($this->id);
}
}
Then you can access property like it was in database:
echo Photo::find(5)->hashed_id;

Laravel Models - add non-database field

I have a model in Laravel called Checkout. It is just tied to a table in the database called checkouts.
namespace App;
use Illuminate\Database\Eloquent\Model;
class Checkout extends Model
{
protected $primaryKey = 'id';
protected $table = 'checkouts';
}
What I would like to do is add a field to the model that isn't a field in the table. Is this even possible?
If need be, I will completely manually build the model, but I have never seen any examples of that either.
Any help would be greatly appreciated! Thanks,
You can use Laravel's Accessor as:
public function getSomeExtraFieldAttribute()
{
return 2*4; // just for exmaple
}
Then you can access it using
$checkout = App\Checkout::find(1);
$checkout->some_extra_field;

Can't eager-load a One-to-One relation in Laravel

In Laravel 5.2 I'm trying to eager load a User along with its Usersettings with this piece of code: App\User::with('usersettings')->get(), but it fails, and I can't seem to figure out why. This is the given error.
BadMethodCallException with message 'Call to undefined method Illuminate\Database\Query\Builder::usersettings()'
I've read the laravel docs and I've watched a lot of Laracasts, and this worked before, so I get the idea I'm missing something really small and probably obvious.
User.php
<?php
namespace App;
/* User with fields: id, email */
class User extends Illuminate\Database\Eloquent\Model {
protected $table = 'users';
public function usersettings() {
return $this->hasOne("App\Usersettings", "userid");
}
}
Usersettings.php
<?php
namespace App;
/* Settings with fields: id, userid, textcolor */
class Usersettings extends Illuminate\Database\Eloquent\Model {
protected $table = 'usersettings';
public function user() {
return $this->belongsTo('App\User', 'userid');
}
}
//Edit: I already tried lowercasing the s. This typo might have snuck in copying it to SO, but the error was there, and still is there, even after fixing it.
//Edit:
<?php
namespace App;
use App\UserSettings;
class User extends Illuminate\Database\Eloquent\Model {
protected $table = 'users';
public function settings() {
return $this->hasOne(UserSettings::class, "userid");
}
}
If I run php artisan tinker>>> App\User::with('settings')->get(), it works as expected, but below
<?php
namespace App;
use App\UserSettings;
class User extends Illuminate\Database\Eloquent\Model {
protected $table = 'users';
public function usersettings() {
return $this->hasOne(UserSettings::class, "userid");
}
}
gives BadMethodCallException with message 'Call to undefined method Illuminate\Database\Query\Builder::usersettings()' when I run php artisan tinker >>> App\User::with('usersettings')->get(). Likewise when I rename the method to abc and run php artisan tinker >>> App\User::with('abc')->get() (that fails as well)
You mistyped when defining the relation on the User model:
public function usersettings() {
return $this->hasOne("App\Usersettings", "userid");
}
I would suggest some cleanup:
name the tables 'users' and 'user_settings',
rename the field 'userid' to 'user_id',
rename the 'Usersettings' model to 'UserSettings'.
That way you dont need to explicitly define table names and foreign keys, because you follow the conventions and Laravel can "guess" those.
You can also rename the the relation 'usersettings()' to 'settings()', since its obvious that they're the users' settings. Then you can fetch it like: 'User::with('settings')->get()'.
Did you try running composer dump-autoload after you changed your model relationships?
Just to make sure you're aware of this: you don't actually need to define the relationship in your UserSettings model to achieve what you want, only define the relationship in the User model. This is because you'd only need to get the settings in the context of the User, not the other way around.
Code for the User model:
<?php
namespace App;
use App\UserSettings;
class User extends Illuminate\Database\Eloquent\Model {
public function userSettings() {
return $this->hasOne(UserSettings::class);
}
}
UserSettings model: make sure it's camel case and that userid is present in your migration (the convention in your case would be to have user_id, so I would rename that column in your migration because then Eloquent will pick it up automatically and you don't need the second parameter in your User model's hasOne() definition).
Also, rename your table to user_settings if you can. That's another Eloquent convention, this one being that the model name UserSettings translates the camel case S letter in the middle of the classname to _ (and as a matter of fact, you shouldn't even need to explicitly state the table name if you've used the name user_settings).
Code for the UserSettings model:
<?php
namespace App;
/* Settings with fields: id, userid, textcolor */
class UserSettings extends Illuminate\Database\Eloquent\Model {
protected $table = 'user_settings';
}
Now, you should be able to do the below action. Note that the with parameter needs to be the relation function name from the User model.
$usersThatHaveSettingsIncludedInTheResults = User::with('userSettings')->get();

Explicitly define $guarded=['id'] to prevent accidental insert?

Say I'd like to apply the following logic to all my Model class:
All Model class with auto incremental id should have protected $guarded = ['id']
So that I can't accidentally insert incremental id myself.
Does it makes sense in Laravel 5?
Do I have other ways to achieve it beside setting protected $guarded = ['id'] in each Model class?
If you follow the accepted answer, you can still manually set the id, and actually, I would actually say that answer makes your application's security worse.
Laravel already makes everything guarded by default:
protected $guarded = ['*'];
As a result, there's no need to specify the id when it's already guarded. When you override that property, you are telling Laravel, "Actually, don't guard everything. Only guard the properties that I specify." So essentially, you are unguarding other properties.
Also, if you are manually going to assign properties like this:
$model = new Model;
$model->id = 1;
$model->name = 'Example';
$model->save();
The $guarded / $fillable properties never kick in. They kick in when you use methods like create, update, fill, etc.
Yes, it still makes sense to do so. While ID is not fillable by default, it can be still overwritten when modified directly with $model->id = $id;
A better way is to define a base class for all your models and set the $guarded property there. This way you only need to do this once.
//Model.php
<?php namespace Your\Model\Namespace;
use Illuminate\Database\Eloquent\Model as Eloquent;
class Model extends Eloquent {
protected $guarded = ['id'];
}
//SomeModel.php
<?php namespace Your\Model\Namespace;
class SomeModel extends Model {}

Categories