laravel relationship one to many - laravel

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();
});
}

Related

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 access related model using mutator during $user->posts()->create([...])

I have User and Post models.
Would like to access the User when the mutator for the post's title is being called at the time of the post creation. Similar to a question from a few years ago.
But I am unable to access the User inside the Posts title mutator.
Tried $this->user->id (preferred). Also tried: $this->user_id, $this->user()->get()->id.
>>> User::first()->posts()->create(['title'=>'test '.now()])
PHP Warning: Attempt to read property "id" on null <<<=== when trying $this->user->id
=> App\Models\Post {#4155
title: "test 2021-05-08 11:41:55", <<<=== title is shown before user_id
user_id: 1, <<<=== but in migration user_id is defined *before* title
updated_at: "2021-05-08 11:41:55",
created_at: "2021-05-08 11:41:55",
id: 1,
user: null, <<<=== note "user" attribute is added as result of the $this->user->id
}
Would think the user is definitely known at that time, as we're using that user to add a post to.
If I make the user_id fillable, then this one works as expected (for that one: the array order matters):
Post::create(['user_id'=>User::first()->id, 'title'=>'test '.now()])
But, that's less... eloquent.
My config + test details:
// Post.model
protected $fillable = [
// 'user_id', // also tried with making user_id fillable
'title',
];
public function setTitleAttribute($value) {
// dump($this->user_id); // null
// dump($this->user); // null (& adds empty user property to this)
// dump($this->user->id); // PHP Warning: Attempt to read property "id" on null (& adds property)
// dump($this->user()->get()); // empty collection Illuminate\Database\Eloquent\Collection
// dump($this->user()->toSql()); // select * from "users" where "users"."id" is null
$this->attributes['title'] = $value;
}
public function user() {
return $this->belongsTo(User::class);
}
// User.model
public function posts() {
return $this->hasMany(Post::class);
}
// posts migration
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained();
$table->string('title');
$table->timestamps();
});
Using mysql as DB.
This seems to be the safest + cleanest: use boot event closure 'creating', which is triggered after the mutators and right before data is saved to the database.
public static function boot() {
parent::boot();
static::creating(function (Post $post) {
// now we have both $post->user and $post->title
dump($post->user->name);
dump($post->title);
});
}
can you provide more detail?
try this
auth()->user()->id
if user is logged in
if u want select user from db
User::where("filed name","value")->first()->id

Updating a Laravel pivot table when the pivot has a unique field

I have two models, User and Service, with a ManyToMany relationship. The pivot table has two additional fields, id_code and alias. There may be many relationships between User and Service, but each one is uniquely identified by the field id_code. I need to retrieve a specific record in the pivot table by the id_code, and update the alias for that record only.
My models:
User:
class User extends Authenticatable
{
//All attributes & other functions here...
public function linked_services(){
return $this->belongsToMany(Service::class)
->withPivot('alias', 'id_code')
->withTimestamps();
}
}
Service:
class Service extends Model
{
//All attributes & other functions here...
public function linked_users(){
return $this->belongsToMany(User::class)
->withPivot('alias', 'id_code')
->withTimestamps();
}
}
Service-User migration:
Schema::create('service_user', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('user_id')->nullable();
$table->unsignedBigInteger('service_id');
$table->string('alias', 50)->nullable();
$table->string('id_code', 50);
$table->foreign('user_id')->references('id')->on('users');
$table->foreign('service_id')->references('id')->on('services');
$table->timestamps();
});
In the update function, I get the specific $id_code from a view, but I don't know how to update the "alias" for that specific "id_code".
What I already tried and is not working:
public function update(Request $request, String $id_code){
foreach(Auth::user()->linked_services as $service){
if($service->pivot->id_code === $id_code){
$service->pivot->alias = $request->alias;
$service->pivot->save();
}
}
return redirect()->route('services_user.index');
}
This function updates ALL the existing pivot records for the service_user, I need to update only the specific record for an given "id_code".
I solved it!!! I tell you how:
Auth::user()->linked_services()
->wherePivot('id_code', $id_code)
->update(['alias' => $request->alias]);
I learned that $user->linked_services() (unlike $user->linked_services) returns a relationship object (in this case, belongsToMany), which contains all the relationship data (intermediate table, pivots, FKs, parent / related objects, etc), and the desired pivot can be accessed on that object with ->wherePivot().

Laravel eloquent attach auto generate random ID

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()]);
}

Laravel 5.2 How laravel treat this belongsto relationship?

I have 3 tables, with the fields listed below:
Pricings
id
route_id
Routes
id
from_city_id
to_city_id
Cities
id
name
So far the relationships of fields are: pricings belong to a route, and routes belongs to city.
But I'm not sure about these relationships, since from_city_id and to_city_id are foreign keys relating to id on cities.
Maybe I'm wrong designing my table or something else.
route_id is a foreign key to id on the Routes table.
from_city_id and to_city_id are foreign keys to id on the Cities table.
How I can define relationships of these 3 tables so I can get from city name and to city name from the pricings model, like $pricing->from_city->name and $pricing->to_city->name?
Any help appreciated.
UPDATE:
My Pricing Model:
public function route()
{
return $this->belongsTo(Route::class);
}
My Route Model:
public function pricing(){
return $this->hasOne(Pricing::class);
}
public function zone(){
return $this->belongsTo(Zone::class);
}
public function city(){
return $this->belongsTo(City::class);
}
public function from_city(){
return $this->belongsTo(City::class);
}
public function to_city(){
return $this->belongsTo(City::class);
}
Now I can use $pricing->route->from_city->name and $pricing->route->to_city->name
It shows the correct result, but how can this be achieved using Laravel?
Does this mean Laravel will assume that the route table has fields to_city_id and from_city_id, since the method in the route model is to_city() and from_city()?
Thanks
One solution may be to make a migration (new table or to change to existing table).
https://laravel.com/docs/5.3/migrations
Laravel's schema build is super handy:
https://laravel.com/docs/5.0/schema
An example of the routes migration would be:
Make the migration:
php artisan make:migration routes
The migration would look something like:
```
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateUserRole extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('routes', function (Blueprint $table) {
$table->increments('id');
$table->foreign('id')->references('route_id')->on('pricings')->onDelete('cascade');
$table->integer('from_city_id')->unsigned()->index();
$table->foreign('from_city_id')->references('id')->on('cities')->onDelete('no action');
$table->integer('to_city_id')->unsigned()->index();
$table->foreign('to_city_id')->references('id')->on('cities')->onDelete('no action');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::drop('routes');
}
}
```
The above for some reason will not show correctly on here, so here is a cleaned up view link: http://viper-7.com/kfgUjt
Try this one:
In Pricing Model:
//if pricing and route have one to one(hasOne) relationship or you may change it to one to many(hasMany)
public function routes(){
return $this->hasOne('App\Routes','id','route_id');
}
and in Route Model:
public function from_city(){
return $this->hasOne('App\Cities','id','from_city_id');
}
public function to_city(){
return $this->hasOne('App\Cities','id','to_city_id');
}

Resources