laravel 4 Cannot Retrieve ALL results ? pivot table Many to Many - php

I am a bit stuck on this...
I have 3 tables: photographers, languages and languages_spoken (intermediate table).
I am trying to retrieve all the languages spoken by a photographer. I defined my models like this:
class Photographer extends Eloquent {
/**
* Defining the many to many relationship with language spoken
*
*/
public function languages() {
return $this->belongsToMany('Language', 'languages_spoken', 'language_id', 'photographer_id');
}
class Language extends Eloquent {
/**
* Defining the many to many relationship with language spoken
*
*/
public function photographers() {
return $this->belongsToMany('Photographer', 'languages_spoken', 'language_id', 'photographer_id')
->withPivot('speakslanguages');
}
This is how I was trying to retrieve all the results for the logged in photographer:
$photographer = Photographer::where('user_id', '=', $user->id);
if ($photographer->count()) {
$photographer = $photographer->first();
// TEST
$spokenlang = $photographer->languages;
die($spokenlang);
// END TEST
} else {
return App::abort(404);
}
The problem is that in my db I have 4 entries for the same photographer. but when I do this I only get the last result...
[{"id":"3","language_name":"Afrikaans","updated_at":"-0001-11-30 00:00:00","created_at":"-0001-11-30 00:00:00","native_name":"Afrikaans","ISO639_1":"af","pivot":{"language_id":"3","photographer_id":"3"}}]
Any idea on what is wrong ?
Thanks a lot for your help!!!

The third parameter to belongsToMany should be the foreign key.
In the Photographer class:
public function languages() {
return $this->belongsToMany('Language', 'languages_spoken', 'language_id', 'photographer_id');
}
...should be:
public function languages() {
return $this->belongsToMany('Language', 'languages_spoken', 'photographer_id');
}
In the Language class:
public function photographers() {
return $this->belongsToMany('Photographer', 'languages_spoken', 'language_id', 'photographer_id')
->withPivot('speakslanguages');
}
Should be:
public function photographers() {
return $this->belongsToMany('Photographer', 'languages_spoken', 'language_id')
->withPivot('column1', 'column2', 'column3'); // withPivot() takes a list of columns from the pivot table, in this case languages_spoken
}
But, since you're not even using strange keys, you don't need to pass that third parameter at all.
So this is just fine:
public function languages() {
return $this->belongsToMany('Language', 'languages_spoken');
}
And:
public function photographers() {
return $this->belongsToMany('Photographer', 'languages_spoken')
->withPivot('column1', 'column2', 'column3'); // withPivot() takes a list of columns from the pivot table, in this case languages_spoken
}

Related

Retrieving data from hasMany Relationship

I want to show data from 'personas' (parent table) that has at least one 'residente' (child table), its a one to many relationship, and i want to show data of that residente too.
I was trying to do it using the has() method like the laravel documentation says:
https://laravel.com/docs/9.x/eloquent-relationships#querying-relationship-existence
but it does not work.
Models looks like this
//in the Persona class
public function residentes()
{
return $this->hasMany(Residente::class);
}
//in the Residente class
public function persona()
{
return $this->belongsTo(Persona::class);
}
//in the PersonasController
public function index()
{
$personas = Persona::has('residentes')->get();
dd($personas);
}
the Result
enter image description here
//it doesn't get the data from "residentes"
Try :
public function index()
{
$personas = Persona::with('residentes')->get();
dd($personas);
}
If you want to search using some keys inside the residentes relationship you can use whereHas
Persona::with('residentes')
->whereHas('residentes',function($residente){
return $residente->where('column_name',$value);
})->get();
Also try to mention the local_key and the foreign_key in the relationship itself reference : https://laravel.com/docs/9.x/eloquent-relationships
return $this->hasMany(Comment::class, 'foreign_key', 'local_key');
Please try the following in the place of key give actual field names.
//in the Persona class
public function residentes()
{
return $this->hasMany(Residente::class, 'foreign_key', 'local_key');
}
//in the Residente class
public function persona()
{
return $this->belongsTo(Persona::class,'foreign_key', 'owner_key');
}
//in the PersonasController
public function index()
{
$personas = Persona::with('residentes')->get();
foreach($personas as $person){
echo "<pre>";
print_r($person->residentes()->get()->toArray());
}
}

What would be the best approach to query eloquent relatioships where keys are on the same table

Considering this table
Services Table
id
company_id
category_id
1
2
4
2
4
6
And this model
CategoryModel.php
public function companies():Attribute
{
return new Attribute(
get: fn () => CompanyModel::whereHas('services', function ($q) {
$q->where('category_id', $this->id);
}),
);
}
Considering that the services table holds both company_id and category_id columns what would be the best approach to query companies relationship that have services under the current category (The companies table does not have a category_id column), my current implemetation is not optimal as it does not allow me to perform any relationship constrains.
EDIT
Each company offers multiple services and each service belongs to a single category.
I also have a reviews table (related to each service) with a rating column
The above query worked efficiently until I needed to constrain/order categories based on the reviews table.
CategoryModel.php
public function scopeHasReviews($query)
{
$query->whereHas('companies', fn ($q) => $q->whereHas('reviews'));
}
This ofcourse will not work since there is no relationship.
Using Attribute in this context is dangerous, because it can lead to N+1 problems. This is a usual many-to-many relationship, so it needs to be implemented in models:
Category.php
public function companies(): BelongsToMany
{
return $this->belongsToMany(Company::class, 'services');
}
Company.php
public function categories(): BelongsToMany
{
return $this->belongsToMany(Category::class, 'services');
}
Further, since it is not clear exactly what problem must be solved - I will write an example with sorting by number of reviews:
Review.php
public function scopeWhereRawService(Builder $query, string $service): Builder
{
return $query->whereRaw('service_id = ' . $service);
}
Company.php
public function reviews(): HasManyThrough
{
return $this->hasManyThrough(Review::class, Service::class);
}
public function categories(): BelongsToMany
{
return $this->belongsToMany(Category::class, 'services');
}
public function categoriesOrderedByReviews(): BelongsToMany
{
return $this->categories()->withCount([
'reviews as reviews_count' => fn(Builder $q) => $q->whereRawService('services.id')
])->orderByDesc('reviews_count');
}
Category.php
public function reviews(): HasManyThrough
{
return $this->hasManyThrough(Review::class, Service::class);
}
public function companies(): BelongsToMany
{
return $this->belongsToMany(Company::class, 'services');
}
public function companiesOrderedByReviews(): BelongsToMany
{
return $this->companies()->withCount([
'reviews as reviews_count' => fn(Builder $q) => $q->whereRawService('services.id')
])->orderByDesc('reviews_count');
}

Laravel Eloquent elation for pivot table

I'm currently working on a laravel project, but I'm kind of stuck finding the right eloquent relations.
My tables and the connections (should) look like this:
Project Relations
My model relations look like this:
User
public function team()
{
return $this->hasMany(Team::class, 'user_id');
}
public function evaluation()
{
return $this->hasMany(Evaluation::class, 'user_id');
}
Team
public function user()
{
return $this->belongsTo(User::class);
}
public function survey()
{
return $this->hasMany(Survey::class, 'team_id');
}
Evaluation
public function user()
{
return $this->belongsTo(User::class);
}
public function survey()
{
return $this->hasMany(Survey::class, 'evaluation_id');
}
Survey
public function team()
{
return $this->belongsTo(Team::class);
}
public function evaluation()
{
return $this->belongsTo(Evaluation::class);
}
public function surveyresponse()
{
return $this->hasMany(SurveyResponse::class, 'survey_id');
}
SurveyResponse
public function survey()
{
return $this->belongsTo(Survey::class);
}
public function testquestion()
{
return $this->belongsTo('App\TestQuestion');
}
Is this the way to go? Do I need a "Has Many Through" relation here? Or a "Polymorphic Relationship"?
Seems correct to me, i just didnt see the TesteQuestion model (your last relation).
Answering your question:
The HasManyThrough relation is just a shortcut for accessing distant relations via an intermediate relation, in your case: Users has many evaluations that has many surveys. With this relationship you could get all surveys from a user.
Your relation would look like this:
/**
* Get all of the surveys for the user.
*/
public function surveys()
{
return $this->hasManyThrough('App\Survey', 'App\Evaluation');
}
You can access this relation like this:
$user->surveys();
But you can achieve the same (without using the HasManyThrough) by doing:
$user->evaluations()->surveys();
Beware that this will return the evaluations too, not just the surveys and it requires more processing.
So i recommend you doing the HasManyThrough relationship if you pretend to access the surveys a lot.

Laravel: Sorting a collection with a many to many relationship

I have two tables: assessments and benchmarks. benchmarks has a field called content. There is a many to many relationship between them: assessment_benchmark. I want to sort a collection of records from the assessment_benchmark table by the content attribute of the corresponding benchmark. I have tried:
$sorted = AssessmentBenchmark::all()->sortBy(function($assessmentBenchmark){
return $assessmentBenchmark->benchmark->content;
});
But this just does not work (it just returns the original order). However, when I return $assessmentBenchmark->comment for example, it does work (comment is a field in assessment_benchmark).
The models look like this:
class AssessmentBenchmark extends Model
{
public function benchmark()
{
return $this->belongsTo(Benchmark::class);
}
public function assessment()
{
return $this->belongsTo(Assessment::class);
}
}
class Benchmark extends Model
{
public function assessments()
{
return $this->belongsToMany(Assessment::class);
}
}
class Assessment extends Model
{
public function benchmarks()
{
return $this->belongsToMany(Benchmark::class);
}
}
Well, you can use below query for sorting, I'm gonna use Assessment model, because, I'm never use pivot modal before. Actually, I never had pivot model..
$assessments = Assessment::with(["benchmarks"=>function($query){
$query->orderBy("content","DESC");
}])
With method aşso provide you eagerloading, so when you put $assessments in iteration , you won't make new query for each relation
From chat discussion, it found that you have pivot field and for that you can change your belongsToMany relationship like this
class Benchmark extends Model
{
public function assessments()
{
return $this->belongsToMany(Assessment::class)->withPivot('comment','score')->withTimestamps();
}
}
class Assessment extends Model
{
public function benchmarks()
{
return $this->belongsToMany(Benchmark::class)->withPivot('comment','score')->withTimestamps();
}
}
Now fetch data
$assessment = Assessment::with(['benchmarks' => function($query){
$query->orderBy('content', 'desc');
}])->find($assessmentId);
In view you can render it like this
#foreach($assessment->benchmarks as $benchmark)
<tr>
<td>{{$benchmark->id}}</td>
<td>{{$benchmark->name}}</td>
<td>{{$benchmark->pivot->score}}</td>
<td>{{$benchmark->pivot->comment}}</td>
</tr>
#endforeach
For update you can use updateExistingPivot
For details check ManyToMany relationship https://laravel.com/docs/5.6/eloquent-relationships#many-to-many

Relationships returning wrong/null data (Laravel 5.2)

Got a domain table which has a One To Many relationship with domain_hosts_table, server_hosts_table and systems_table. So far so good.
Calling the table data:
$domains = Domain::with('domain_host', 'server_host', 'system')->get();
Domain model :
public function domain_host()
{
return $this->hasOne('App\DomainHost', 'id');
}
public function server_host()
{
return $this->hasOne('App\ServerHost', 'id');
}
public function system()
{
return $this->hasOne('App\System', 'id');
}
DomainHost, ServerHost, System model :
public function domains()
{
return $this->hasMany('App\Domain');
}
Domains table :
So far so good.
Let's take a look at what this particular table returns while being foreached.
The first 2 rows should be the same (basing on their IDs), and all rows after the first 2 are just empty.
(dd of the fetched data, notice the relations being empty at 4th object, 1st object actually has data).
Had to define another parameter when defining my relationships:
public function domain_host()
{
return $this->hasOne('App\DomainHost', 'id', 'domain_host_id');
}
public function server_host()
{
return $this->hasOne('App\ServerHost', 'id', 'server_host_id');
}
public function system()
{
return $this->hasOne('App\System', 'id', 'system_id');
}
It was looking for the ID of the current row in the other table.

Categories