Using Eloquent Relationships - laravel

Laravel 8
I have a couple models using eloquent relationships.
A User which can have many blog posts
public function blogPosts() {
return $this->hasMany(BlogPost::class);
}
And a BlogPost which belongs to the author
public function author() {
return $this->belongsTo(User::class);
}
My issue is that when I attempt to use one of these relationships, I am returned an empty object.
We can focus on the belongsTo() relationship.
I am attempting to use this relationship here:
class BlogPostResourceCollection extends ResourceCollection
{
/**
* Transform the resource collection into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return $this->collection->map(function($post) {
return [
'id' => $post->id,
'author' => $post->author(),
'title' => $post->title,
];
});
}
}
Why am I getting an empty object for author?
Edit: the blog post table does have a foreign key user id column.

All the problem is laravel by default get the relation from the method name and you add custom names so to fix it you need to add the second argument for each relation like this
User Model
public function blogPosts() {
return $this->hasMany(BlogPost::class, 'user_id');
}
BlogPost
public function author() {
return $this->belongsTo(User::class, 'user_id');
}
At the end in your BlogPostResourceCollection you need to access the author data all you need is to write ->author not ->author()
for more info, you could check the One-To-Many relation here

Related

How to update one to many polymorphic relationship?

How to update multiple records in One to many polymorphic relationship?
I want to update the fields as a group, but how?
Skill Model:
class Skill extends Model
{
use HasFactory;
protected $fillable = ['title', 'percentage'];
/**
* Get the owning skillable model.
*/
public function skillable(): MorphTo
{
return $this->morphTo();
}
}
User Model:
class User extends Model
{
use HasFactory;
/**
* Get all of the skill's user.
* #return MorphMany
*/
public function skills(): MorphMany
{
return $this->morphMany(Skill::class, 'skillable');
}
}
There are a number of ways to do so:
$user->skills->each(function ($skill) {
$skill->update([...]);
});
$user->skills->each(fn($skill) => $skill->update([...]));
$user->skills->each->update([...]);
$user->skills()->update([...]);
I recommend the first three approaches. Because if there are any model events, those will be fired. Model events won't be fired in the fourth one.
Specifically to your problem, you might want to do something like this in the controller:
public function update()
{
$skills = collect(request('skill_titles'))
->zip(request('skill_percentages'))
->map(function ($pair) {
return [
'title' => $pair[0],
'percentage' => $pair[1],
]
});
$skills->each(function ($skill) use ($user) {
$user->skills()->where('title', $skill['title'])
->update($skill['percentage']);
});
}
you can use update method
// make sure you have the desired attributes in fillable array property in your model class
$model->related_model->update([inputs]);
if you write the relationship methods "related_method" in model class correctly you can use them as properties of your model and access their attributes or update them.

Laravel Nova Actions BelongsTo field not working

I have this simple action:
/**
* Perform the action on the given models.
*
* #param \Laravel\Nova\Fields\ActionFields $fields
* #param \Illuminate\Support\Collection $models
* #return mixed
*/
public function handle(ActionFields $fields, Collection $models)
{
foreach ($models as $model) {
$model->update([
'user_id' => $fields->user
]);
}
}
/**
* Get the fields available on the action.
*
* #return array
*/
public function fields()
{
return [
BelongsTo::make('User', 'user', User::class),
];
}
At first, it seems fine, but when I select User from BelongsTo relation and try to save exception is throwing:
Argument 1 passed to Laravel\Nova\Fields\BelongsTo::getRelationForeignKeyName() must be an instance of Illuminate\Database\Eloquent\Relations\Relation, instance of Illuminate\Support\Fluent given, called in /Users/rd/Sites/bns-crm/vendor/laravel/nova/src/Fields/BelongsTo.php on line 212
Yes i know i'm late but - here's a solution for this:
Use a Select-Field instead of BelongsTo and Pluck your options to build Key-Value pairs:
public function fields()
{
return [
Select::make('debitor')->options(\App\Models\Debitor::pluck('Name', 'id'))
];
}
Then in the handle you should geht the ids in $fields:
public function handle(ActionFields $fields, Collection $models) {
Log::info($fields);
}
Maybe I'm late, but, for the ones like me wanting to use the BelongsTo searchable field because the model they want to search in contains too much records to pack them in a normal Select field here is the solution I found:
Create a class in App\Nova\Fields with this code:
<?php
namespace App\Nova\Fields;
use Laravel\Nova\Fields\BelongsTo;
use Laravel\Nova\Http\Requests\NovaRequest;
class BelongsToForActions extends BelongsTo
{
public function fillForAction(NovaRequest $request, $model)
{
$attribute = $this->attribute;
if ($request->exists($attribute)) {
$value = $request[ $attribute ];
$model->{$attribute} = $this->isNullValue($value) ? null : $value;
}
}
}
Then use it like you would use a normal BelongsTo field. Just remember to fill the 3 arguments on the make, so, for example:
BelongsToForActions::make('User', 'relation', \App\Nova\User::class)->searchable()
Remember that 'relation' must exist.
Check your namespaces. Did you imported right class? User class must be resource class
public function fields()
{
return [
BelongsTo::make('User', 'user', User::class),
];
}
I actually fixed this by mocking the key value pair used in this relationship.
First I build an array with the ID column as key and the name column as value.
$clients = Client::all()
->keyBy('id')
->map(fn($client): string => $client['name'])
->toArray();
Then I use the Select nova field to display it.
Select::make('Klant', 'client')
->searchable()
->options($clients)
->rules('required'),

Strange behavior on laravel many to many relationship

I have two models User and Tenant and in my project, a User can have many Tenants connected to him and Tenant can have many users connect to him.
This is my User model
public function tenants()
{
return $this->beLongsToMany(\App\Models\TenantsUsers::class, 'tenants_user', 'user_id', 'tenant_id');
}
This is my Tenant model
public function users()
{
return $this->beLongsToMany(\App\Models\TenantsUsers::class, 'tenants_user', 'tenant_id', 'user_id');
}
And this is my TenantsUsers model
class TenantsUsers extends Model
{
use UtilTrait;
use Notifiable;
protected $table = 'tenants_user';
protected function serializeDate(DateTimeInterface $date)
{
return $date->format('Y-m-d H:i:s');
}
/**
* The attributes that should be casted to native types.
*
* #var array
*/
protected $casts = [
'user_id' => 'integer',
'tenant_id' => 'integer'
];
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
**/
public function tenants()
{
return $this->hasMany(\App\Models\Tenant::class, 'tenant_id');
}
public function users()
{
return $this->hasMany(\App\Models\User::class, 'user_id');
}
When I execute this function from the repository:
$userTemp = $this->userRepository->with(['tenants'])->findWhere(['email' => $userEmail])->first();
And I'm getting this error :
SQLSTATE[42712]: Duplicate alias: 7 ERROR: table name "typo_tenants_user" specified more than once (SQL: select
"typo_tenants_user".*, "typo_tenants_user"."user_id" as "pivot_user_id", "typo_tenants_user"."tenant_id" as
"pivot_tenant_id" from "typo_tenants_user" inner join "typo_tenants_user" on "typo_tenants_user"."id" =
"typo_tenants_user"."tenant_id" where "typo_tenants_user"."user_id" in (1))
What I'm doing wrong?
You don't need to create a model for pivot tables in Eloquent many-to-many relationships. Instead, use the related model's class when defining the relationship:
// User model
public function tenants()
{
return $this->belongsToMany(\App\Models\Tenant::class, 'tenants_user', 'user_id', 'tenant_id');
}
// Tenant model
public function users()
{
return $this->belongsToMany(\App\Models\User::class, 'tenants_user', 'tenant_id', 'user_id');
}
If you follow Eloquent naming conventions by defining the pivot table as tenant_user rather than tenants_user, things can be even further simplified to:
// User model
public function tenants()
{
return $this->belongsToMany(\App\Models\Tenant::class);
}
// Tenant model
public function users()
{
return $this->belongsToMany(\App\Models\User::class);
}

How to add the condition on inter table not relation table when defining a model relation

I have a relation table named company_team it relation to company and user, and there is a field is_ceo in company_team table to flag weather a company team member is an ceo. following is my model define
class CompanyTeam extends Pivot
{
/**
* return all company team member with out ceo
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function team_member()
{
return $this->belongsTo(User::class, 'user_id');
}
/**
* return only ceo's info
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function company_ceo()
{
$this->where('is_ceo', 1); // it dosen't work, but it is effect l want
return $this->belongsTo(User::class, 'user_id');
}
}
l searched an answer with using addGlobalScope, but it doesn't fit me, because when l use both of team_member and company_ceo relation, it will add condition on both of then
In user model, you can define a scope
public function scopeCompanyCeo($query){
return $query->where('is_ceo',1);
}
then you can use like in your controller
$user = User::find(1)->companyCeo()->get();
You can't add conditions when defining the relationship.
You have to add the condition when performing the queries.
CompanyTeam::with(['company_ceo' => function ($q) use() {
$q->where('is_ceo', 1);
}])
->get();
public function company_ceo()
{
return $this->belongsTo(User::class, 'user_id')->where('is_ceo', 1);
}
I hope this helps. Have a great day and happy coding... :)

Amending foreign key on a Laravel relationship

In Laravel (v5.7.12), I have two models - User and Project.
A user has an id and can have many projects. A project has an owner_id.
I can't seem to configure the relationship correctly. In my user model, I have:
/**
* Get the projects associated with the user.
*/
public function projects()
{
$this->hasMany('\App\Project', 'owner_id', 'id');
}
In my project model, I have:
/**
* Get the owner associated with the user.
*/
public function owner()
{
$this->belongsTo('\App\User', 'id', 'owner_id');
}
But calling either $user->projects() or $project->owner() returns null.
How should I configure my non-standard relationship keys?
You forgot to return the method:
public function projects()
{
return $this->hasMany('\App\Project', 'owner_id');
}
Do this also for the second one:
public function owner()
{
return $this->belongsTo('\App\User', 'owner_id');
}

Resources