Relation with condition in laravel - laravel

I have 3 types of author in my app. In table of post author stored in column post_author like "AuthorType_id", where AuthorType can be user, group or system.
The question is: can I create a conditional relation?
What this means.
Relation author() checks a value and return related model.
Data in column stores like "user_123", "system_123", "group_123" where user, system or group is AuthorType and 123 - is id of record in related table.
I'm really confused about this case.
I'm using laravel 6.
Ex:
public function author() {
$type = explode("_", $this->post_author);
switch($type[0]) {
case "user":
return $this->hasOne('App\User', 'id', $type[1]);
case "group":
return $this->hasOne('App\UserGroup', 'id', $type[1]);
default:
return $this->hasOne('App\BluePoster', 'id', $type[1]);
}
}

Related

Laravel: Get all projects and check if user has liked them

I want to get all projects, and foreach project I want to get a property for example is_liked which indicates that if the current_user (I've his Id) has liked this project or not.
User Model
class User extends Model
{
public function projects()
{
return $this->hasMany(Project::class);
}
public function favorite_projects()
{
return $this->belongsToMany(Project::class, 'favorite_projects', 'user_id', 'project_id')->withTimestamps();
}
}
Project Model
class Project extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
}
User Table
- id
- name
Project Table
- id
- user_id
- title
favorite_projects table
- user_id
- project_id
What is the best way to get this done ? the data I want to get is something like this:
[
{
"id": 1,
"user_id": 3,
"title": "my title",
"is_liked": true
}
]
Note: the current_user->id might or might not be same as the project->user_id.
$projects = DB::table('projects')
->join('favorite_projects', 'favorite_projects.project_id', '=', 'projects.id')
->select('projects.*', 'favorite_projects.*')
->where('projects.user_id', Auth::id())
->get();
This join statement allows you to join 2 tables together and for each project that is being favorited by the user, you will see the favorite record attached with the record.
Perform a dd($projects) after that SQL query and see what the result is like, and then for each result, you set $project['is_liked'] accordingly.

Eloquent Api Resource: show one column from pivot table

I have a vehicle database with a many to many relation with my variant and country table. I only want to show the title that's in my pivot (countries_variants) table. But when I set the relation in my api resource file, it shows all the columns from variants, countries_variants and countries table. Is there a way to only get the title from the pivot table?
Here is my Variant Model:
public function countries(): BelongsToMany
{
return $this->belongsToMany(Country::class, 'countries_variants')->withPivot('title');
}
VariantResource.php
public function toArray($request)
{
return [
'title' => $this->countries->first->title,
];
}
VariantController.php
public function index()
{
return new VariantCollection(Variant::paginate(50));
}
The output I'm getting is:
{
"data": [
{
"title": {
"id": 1,
"title": "Nederland",
"country_code": "NL",
"language_code": "NL_nl",
"pivot": {
"variant_id": 1,
"country_id": 1,
"title": "3/5 deurs"
}
}
}
]
}
I just want to show "title": "3/5 deurs" and not the other data.
I thought that if I set withPivot('title') in my model, it will only show that title and not the foreign keys (variant_id and country_id). Apparently thought wrong..
I tried adding this as well:
'variant_title' => $this->whenPivotLoaded('countries_variants', function () {
return $this->pivot->title;
}),
But the data then returns empty.
Any help would be very much appreciated :)
Try changing
$this->countries->first->title
To
$this->countries->first->title->pivot->title
When you do withPivot('title') you are telling Eloquent to also get title column, if you do not do that, you will only get the keys (variant_id and country_id in your case).
More info here.
Okay so I finally figured it out. I looked through the official laravel docs again and I found this.
So I changed my CountryVariant model class to extend Pivot instead of Model.
Like so:
use Illuminate\Database\Eloquent\Relations\Pivot;
class CountryVariant extends Pivot {
public function variant(): BelongsTo
{
return $this->belongsTo(Variant::class);
}
}
Now in my VariantResource.php I added this:
public function toArray($request): array
{
$items = [];
foreach ($this->countries()->get() as $country) {
$items[$country->language_code] = $country->pivot->only('title');
}
return $items;
}
I also changed the pivot table names to be singular like: country_variant (I had it plural).
Apparently the problem was that in my model it was looking for a primary key which I didn't have in my pivot table. I only had foreign keys and an additional column: 'title'. It took one of my foreign keys and used it as a primary one. So when I extended with extends pivot it would ignore the primary key and I could collect my alternate column 'title' by using country->pivot->only('title') in my resource file.
I hope this helps some one else.

Eloquent relationship: return just one value not entire row

In my laravel project I built a comment section and want to display the names of the people commenting beside their comment.
Initially I have just the userID, so I built a relationship (hasOne) linking the comment table (comment & authorID) to the users table (id (authorID) & username)
Comment.php (model) is:
[..]
public function author()
{
return $this->hasOne(User::class, 'id', 'Author');
}
The User.php model is:
<?php
[..]
public function author()
{
return $this->hasMany(Comment::class, 'Author', 'id');
}
In the controller I get the data with:
$comments= Comments::where('LinkID', (string) $id)->with('author')->orderBy('updated_at', 'ASC')->get()->all();
This works but it gives me the entire row of the user per comment. For security reasons I just want to return the 'name' field of the row (username) without the rest (email, timestamps etc.).
How can I achieve this?
please try:
$comments= Comments::where('LinkID', (string) $id)->with(['author' => function ($q) {
$q = $q->select('name', 'id');
return $q;
}
])->orderBy('updated_at', 'ASC')->get()->all();
or another way:
$comments= Comments::where('LinkID', (string) $id)->with('author:id,name')->orderBy('updated_at', 'ASC')->get()->all();
see eager loading (section Eager Loading Specific Columns)
https://laravel.com/docs/7.x/eloquent-relationships#eager-loading
note that including 'id' is necessary because it 's responsible for the relation

Laravel: How to write a join count query on belongsToMany relationships?

I got the following:
User, Role, with a role_user pivot table and a belongsToMany
relationship
User, Location, with a location_user pivot table and a belongsToMany relationship
There's 2 roles for the user: owner & gardener
Location has a 'gardeners_max' field
In model Location:
protected $appends = ['is_full'];
public function getIsFullAttribute()
{
return $this->attributes['name'] = $this->remainingGardeners() <= 0;
}
public function countGardeners()
{
return $this->gardeners()->count();
}
public function remainingGardeners()
{
return $this->gardeners_max - $this->countGardeners();
}
Now, doing that :
Location::all();
I get that :
[
{
name: 'LocationA',
gardeners_max: 3,
owners: [...],
garderners: [...]
...
is_full: false
}
]
which is cool. BUT... it's not possible to do a WHERE clause on the appended attribute.
Location::where('is_full',true)->get() // Unknown column 'is_full' in 'where clause'
So i'd like to write a join query so I can do a where clause on is_full
And I just can't find the way. Any help will be greatly appreciated!
IMPORTANT:
I know the filter() method to get the results but I need to do a single scopeQuery here
You could try to manipulate the Collection after loading the object from database:
Location::get()->where('is_full', true)->all();
(You have to use get first then all, not sure it works otherwise)
Not sure it's optimized thought.
You can make scope in your location model like this
public function scopeFull(Builder $query)
{
return $query->where('is_full', true);
}
Now you just get all location like this
Location::full()->get();

Get Collection With HasOne Relationship

In my User model in Laravel 5.2 I have a relationship setup with their status to the company.
public function companyStatus()
{
return $this->hasOne('CompanyUser')->select('status');
}
The CompanyUser table has a company_id, user_id, and status field
Then in my controller I do the following:
$company = Company::find($company_id);
$users = CompanyUser::where('company_id', $company_id)->pluck('user_id')->toArray();
$user_data = User::with('companyStatus')->find($users);
but when I dump the user_data array it has all of the users related to the company, but just shows null for their status relationship
{
"id":2,
"name":"Moderator",
"email":"mod#company.com",
"created_at":"2016-09-08 15:26:20",
"updated_at":"2016-09-08 15:26:25",
"company_status":null
}
If I however return just the User collection to the view, and iterate over each user and run
$user->companyStatus->status
the value displays, but I am trying to include this within the collection for a JSON API to consume.
UPDATE
I tried adding the foreign key to the select call on my relationship method:
public function companyStatus()
{
return $this->hasOne('CompanyUser')->select('status', 'user_id');
}
and it now returns the following:
{
"id":2,
"name":"Moderator",
"email":"mod#company.com",
"created_at":"2016-09-08 15:26:20",
"updated_at":"2016-09-08 15:26:25",
"company_status": {"status":"1","user_id":"2"}
}
Not sure if this is the best/correct method or not though.
Okay I figured it out.
I tried adding the foreign key to the select call on my relationship method:
public function companyStatus()
{
return $this->hasOne('CompanyUser')->select('status', 'user_id');
}
Then that returns:
{
"id":2,
"name":"Moderator",
"email":"mod#company.com",
"created_at":"2016-09-08 15:26:20",
"updated_at":"2016-09-08 15:26:25",
"company_status": {"status":"1","user_id":"2"}
}
Without the foreign key Laravel obviously can't determine the related data on the other table.

Resources