Laravel eloquent attach auto generate random ID - laravel

I have an eloquent many to many relationship and I want to use attach() to easily create role_permissions data but the problem is I'm using an UUID for my ID and it throws an error Field 'id' doesn't have a default value. Any way of hijacking the attach() method? so I can set my UUID?
My migration
Schema::create('role_permissions', function (Blueprint $table) {
$table->increments('count')->unique();
$table->string('id')->unique();
$table->string('role_id');
$table->string('permission_id');
$table->timestamps();
});
My model
class Role extends Model
{
//
public $incrementing = false;
public function users()
{
return $this->belongsToMany('App\User', 'user_roles', 'role_id', 'user_id');
}
public function permissions()
{
return $this->belongsToMany('App\Permission', 'role_permissions', 'role_id', 'permission_id');
}
}
My attach code
$role->permissions()->attach($permission_ids);
I know the problem here is that my id is not an incrementing number it's an unique string. My question is how do I "Inject" that unique string to the attach() method? Thank you guys.

The error
Field 'id' doesn't have a default value
refers to the fact that your database does not know how to fill the id field when it's not specified.
Either you edit the schema adding a nullable:
Schema::create('role_permissions', function (Blueprint $table) {
$table->increments('count')->unique();
$table->string('id')->unique()->nullable(); // Bad idea
$table->string('role_id');
$table->string('permission_id');
$table->timestamps();
});
or injecting it via attach:
$role->permissions()->attach($permission_ids, ["id" => null]);
More info on Laravel official doc
Update
For the future developers who encounter this problem you can also set anything inside the attach array, for example:
$role->permissions()->attach($permission_ids, ["id" => Uuid::generate()]);
Update 2
There's also a more clean way to handle this to be honest. I will try to explain it.
You can handle the Pivot events inside the event service provider by simply hooking into the bootmethod:
Here's a snippet
/App/Providers/EventServiceProvider.php
public function boot()
{
Pivot::creating(function($pivot) {
if ($pivot->getTable() == 'role_permissions') {
$pivot->id = Uuid::generate();
}
});
}
Be aware I do not know if this is actually possible on your laravel version. Mine (5.4.*) works as intended

Okay managed to fixed it, what I did with the help of #Claudio Ludovico Panneta's tip.
foreach($permission_ids as $permission_id)
{
$role->permissions()->attach($permission_id, ["id" => Uuid::generate()]);
}

Related

Define additional relationship on many-to-many polymorphic relationship

I'm creating an taggable table like so:
Schema::create('taggable', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('tag_id');
$table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade');
$table->unsignedBigInteger('taggable_id');
$table->string('taggable_type');
$table->unsignedBigInteger('company_id');
$table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade');
$table->unsignedBigInteger('created_by')->nullable();
$table->foreign('created_by')->references('id')->on('users')->onDelete('set null');
$table->timestamps();
});
As you can see, next to connecting tags to a Post, Video etc (as per the Laravel docs example), I'd also like to ensure that the row that's added is connected to a Company and User model so I can keep track who it belongs to and who created it, but even more so access properties from those models in controllers and views.
I know that in my Post model I can do:
public function tags()
{
return $this->morphedByMany(\App\Models\Tag::class, 'taggable')->withPivot('created_by', 'company_id', 'created_at');
}
The problem is that this will retrieve just the value for created_by and company_id and not the Eloquent model. Is this possible?
So what I'd like to do is access properties of those relationships in controllers and views like so:
$post = Post::findOrFail(1);
foreach($post->tags as $tag) {
$tag->created_by->name // accessing 'name' property on the `User` model
}
foreach($post->tags as $tag) {
$tag->company->address // accessing `address` property on the `Company` model
}
You must do like below:
first you must define relationship between tags and users
class Tags extends Model
{
public function taggable(): MorphTo
{
return $this->morphTo();
}
public function createdBy(): BelongsTo
{
return $this->belongsTo(User::class, 'created_by');
}
}
then for achieve that you want you must:
$post = Post::first();
$users = $post->tags()->with('createdBy')->get();

Laravel : How to add conditional clause on a model having Many to Many model relationship?

I have this method on my User Model.
public function reviews()
{
return $this->belongsToMany(Review::class, 'review_user')->withTimestamps();
}
and its inverse here at Review Model.
public function reviewers()
{
return $this->belongsToMany(User::class, 'review_user')->withTimestamps();
}
They are connected via a pivot table review_user with following structure.
$table->id();
// User ID
$table->unsignedBigInteger('user_id')->nullable();
$table->foreign('user_id')
->references('id')
->on('users');
// Review ID
$table->unsignedBigInteger('review_id')->nullable();
$table->foreign('review_id')
->references('id')
->on('reviews');
$table->timestamps();
The review model has a boolean column status.
What I want is a users all reviews which has a status of true?
I have been able to achieve users all reviews using Auth::user()->reviews()->get();
How can I add a where clause, which can filter by status column ?
You can achieve this by using scope or Defining same Relationship with Extra Where Clause
Using Relation:
public function Activereviews()
{
return $this->belongsToMany(Review::class, 'review_user')
->whereStatus(true)
->withTimestamps();
}
OR Using Scope:
public function scopeActivereviews()
{
return $this->reviews()
->whereStatus(true);
}
Now you can do:
Auth::user()->ActiveReviews()->get();
Hope it helps.
Thanks
Try this code like that, does works
$reviews = auth()->user()->whereHas('reviews', function ($query) {
return $query->where('status', '=', true);
})->get();
And you check the official documentation
like option use documentation
Like all other relationship types, you may call the roles method to continue chaining query constraints onto the relationship:
$roles = App\User::find(1)->roles()->orderBy('name')->get();

Laravel Nova show Many-to-Many with pivot

I have two tables show_types and venues. With a many-to-many relationship i did create a new pivot as follows
public function up()
{
Schema::create('show_types_venues', function (Blueprint $table) {
$table->integer('show_types_id')->unsigned();
$table->foreign('show_types_id')->references('id')->on('show_types');
$table->integer('venue_id')->unsigned();
$table->foreign('venue_id')->references('id')->on('venues');
});
}
And added to the models
ShowType.php
public function venues()
{
return $this->belongsToMany(Venue::class, 'show_types_venues', 'show_types_id', 'venue_id');
}
Venue.php
public function shows()
{
return $this->belongsToMany(ShowType::class, 'show_types_venues', 'venue_id', 'show_types_id');
}
I'm trying to make this relation show up on Nova using
ShowType.php
public function fields(Request $request)
{
return [
ID::make()->sortable(),
Text::make('Name'),
BelongsToMany::make('Venues', 'venues', Venue::class)
];
But nothing shows up in my interface. How can i make it show a select multiple where I could add/edit the venues associated with this showType?
Do i need to create something more "manual" ? Thanks
Without luck with the documentation of Nova, I did a workaround using Bejacho's Belongs to Many Fields in which we can easily add the many to many relationships appearing as a multiple selector on Nova.
Having the same setup as i mentioned above the only thing i did was to follow their example:
use Benjacho\BelongsToManyField\BelongsToManyField;
public function fields(Request $request){
return [
..., //If you are using with BelongsToMany native Field, put this field after
BelongsToManyField::make('Role Label', 'roles', 'App\Nova\Role'),
];
}
And worked perfectly for what I needed!

laravel relationship one to many

before anything ,Highly appreciated in advance for help . i have two model
user:
public function posts(){
return $this->hasMany('App\Post');}
post:
public function user(){
return $this->belongsTo('App\user');}
my post have column "user_id" so every post have owner "user_id".
here is my question: i want to delete user but nothing happen to related post. right now i can do this ,but my problem is post_id column have id belongs to deleted user. i want to change that to "null" or "0".
You can do this with model events. Something like this on your User model.
public static function boot() {
parent::boot();
static::deleting(function($user) {
$user->posts()->update(['user_id' => null]);
});
}
Also make sure the user_id field is nullable on the migration.
Just make sure that your post_id field is set to nullable in your migration AND in your database.
You can do this with SQL power - add a migration to your table "posts"
...
$table->foreign('user_id')
->references('id')->on('users')
->onDelete('set null');
It will create a foreign key which sets null to related field on deleting user row
You can change the function boot() from User model:
protected static function boot() {
parent::boot();
static::deleting(function($user) {
$user->posts()->delete();
});
}

users and roles tables relationship through middleware parameter laravel 5.1

I'm developing a user management website using laravel 5.1.
Users are not allowed to register but they are able to see their profiles. Registration can be done only by the admin, so i have two types of users (Admin and normal users).
I'm following this tutorial:
https://www.youtube.com/watch?v=8AQLUZ0X9ME
everything was ok until I reached to the User Model
User table:
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('email')->unique();
$table->string('password', 60);
$table->rememberToken();
$table->softDeletes();
$table->integer('role_id')->unsigned();
$table->timestamps();
});
Role table:
public function up()
{
Schema::create('roles', function (Blueprint $table) {
$table->increments('id');
$table->string('role_name');
//
});
}
Role Model:
class Role extends Model
{
protected $table = 'roles';
protected $fillable = ['role_name'];
public function users()
{
return $this->hasMany('App\User');
}
User model:
public function role(){
return $this->belongsTo('App\Role','role_id');
}
public function hasRole($title){
$user_role = $this->role();
if(!is_null($user_role)){
$user_role = $user_role->role_name; //here is the problem with role_name
}
return ($user_role===$title)?true:false;
}
In PHPStorm the role_name is highlighted in yellow and it says
Field 'role_name' not found in class
\Illuminate\Database\Eloquent\Relations\BelongsTo less... (Ctrl+F1)
Referenced field is not found in subject class. Note: Check is not
performed on objects of type "stdClass" or derived.
I created 3 middlewares Update, Create, and Delete and they all have the same handle function:
public function handle($request, Closure $next, $Admin)
{
$User = $request->user();
return ($User->hasRole($Admin))?$next($request):response(view('errors.401'),401);
}
Routes file:
Route::get('/users','UserController#index');
Route::post('/users/create',['middleware'=>'create:Admin','uses'=>'UserController#store']);
Route::patch('/users/{id}',['middleware'=>'update:Admin','uses'=>'UserController#update']);
Route::delete('/users/{id}',['middleware'=>'delete:Admin','uses'=>'UserController#destroy']);
whenever i open up the create page i got this error:
"ErrorException in C:\wamp\www\laravelU\project - Copy5\app\User.php
line 42: Undefined property:
Illuminate\Database\Eloquent\Collection::$role_name"
I have been dealing with this code for 3 nights i need your help.
if there is any easier way to achieve my goal without using packages ?
The reason you are getting an error when trying $user_role->role_name is because you're technically looking for the property role_name in the BelongsTo class.
When trying to access a relationship of an eloquent model you can either use it as a dynamic property i.e.
$user_role = $this->role;
or
$user_role = $this->role()->get();
So you can change your hasRole method to something like:
public function hasRole($title){
if(is_null($this->role)){
return false;
}
return $this->role->role_name === $title;
}
The reason your application is throwing a MethodNotAllowedHttpException is because you are trying to access a POST route with a GET request. (anytime you navigate to a page using a web browser it will be a GET request).
What you should do is change the Route::post('create' ... to Route::get('create'... ad add something like Route::post('store' to submit to.
Have a look at https://laravel.com/docs/5.1/controllers#restful-resource-controllers
Secondly, you don't need to have 3 different middleware classes if they are just checking if the user is an admin.
Lastly, have a look at https://laravel.com/docs/5.1/validation#form-request-validation (including the Authorizing section)
Hope this helps!
Thanks Ross !
here is my routes after editing them:
Route::get('/users','UserController#index');
Route::get('/users/create',['middleware'=>'create:Admin','uses'=>'UserController#create']);
Route::post('/users/create',['middleware'=>'create:Admin','uses'=>'UserController#store']);
Route::patch('/users/{id}',['middleware'=>'update:Admin','uses'=>'UserController#update']);
Route::delete('/users/{id}',['middleware'=>'delete:Admin','uses'=>'UserController#destroy']);
I get this error when i open up the browser and trying to access the create page :
"ErrorException in C:\wamp\www\laravelU\project - Copy5\app\User.php
line 42: Undefined property:
Illuminate\Database\Eloquent\Collection::$role_name"
in the future, i will add one more role like managers who wont be able to delete users. so i feel it is easier using multiple middlewares but $role_name is pissing me off !

Resources