Laravel hasMany relationship not resturning values without parentheses - php

So I have this model, say City. And it has a OneToMany relationship with another model, say, Citizen.
On the city model, I have defined a relationship helper function
public function citizens()
{
return $this->hasMany(Citizen::class, 'city_id', 'id');
}
Now my problem is that, in a command, I have :
$cities = City::with('citizens')->get();
foreach ($cities as $city) {
$citizens = $city->citizens->pluck('user');
}
Yet it doesn't return anything. To get values I must turn this line to
$cities = City::all();
foreach ($cities as $city) {
$citizens = $city->citizens()->get()->pluck('user');
}
Does anyone have a clue how this might happen ? This started happening today with no apparent reason.
EDIT
To further illustrate the situation,
$cities = City::with('citizens')->get();
foreach ($cities as $city) {
dd($city->citizens()->count()); // => 5
dd($city->citizens->count()); // => 0
}
Here are the models definitions
// City.php
class City extends Model
{
use Searchable;
protected $table = 'cities';
public $incrementing = false;
protected $perPage = 20;
protected $fillable = [
'name',
'unique_code',
'extra_attributes'
];
protected $casts = [
'id' => 'string',
'codes' => 'array',
'extra_attributes' => SchemalessAttributes::class,
];
public static function boot()
{
parent::boot();
self::creating(function ($model) {
$model->id = $model->id ?: Str::orderedUuid();
});
}
public function toSearchableArray(): array
{
return [
'name' => $this->name,
];
}
public function citizens()
{
return $this->hasMany(Citizen::class, 'city_id', 'id');
}
}
// Citizen.php
class Citizen extends Model
{
public $incrementing = false;
protected $perPage = 20;
protected $table = "citizens";
protected $fillable = [
'user_id',
'level_id',
'city_id',
];
public static function boot()
{
parent::boot();
self::creating(function ($model) {
$model->id = $model->id ?: Str::orderedUuid();
});
}
public function user() {
return $this->hasOne(User::class, 'id', 'user_id')->withTrashed();
}
public function city() {
return $this->hasOne(City::class, 'id', 'city_id');
}
}

the relation between City and Citizen is City hasMany Citizens...
In Laravel, hasMany relation reverse is belongsTo Not hasOne, see Laravel doc
so you should correct the relation In Citizen Model like this:
public function city() {
return $this->belongsTo(City::class, 'city_id');
}

There are two issues, one of which OMR has highlighted (you've used an incorrect relationship in your Citizen class), but that isn't the main issue. You're trying to pluck another relationship but unless you explicitly tell it to, Laravel won't eager load that relationship. You've only told it to eager load the Citizen relationship, not the User relationship. Thankfully, Laravel does support nested relationships. You need to update your query thusly:
$cities = City::with('citizens.user')->get();

The best way to solve this issue is by eager loading the citizens when you're getting your cities, that way you wont be executing too many queries (as you will have to get citizens for each city individually), it will save you a lot of execution time in the future when the database gets bigger if you do this :
$cities = City::with('citizens')->get();
foreach($cities as $city) {
$items = $city->citizens->pluck('...');
}

Related

Display table data without user auth

I'm studying event message board. I can display table data by every Users own post. however I would like to display All post too. I wrote this as $tasksall but it didn't work. Could someone teach me what is wrong?
AController.php
public function index()
{
$tasks = Auth::user()
->tasks()
->orderBy('is_complete')
->orderByDesc('created_at')
->paginate(5);
$tasksall =
->tasks()
->orderBy('is_complete')
->orderByDesc('created_at')
->paginate(5);
return view('tasks', [
'tasks' => $tasks, 'tasksall' => $tasksall
]);
}
Task.php (model)
class Task extends Model
{
protected $casts = [
'is_complete' => 'boolean',
];
protected $fillable = [
'title',
'is_complete',
];
public function user()
{
return $this->belongsTo(User::class);
}
}
AController.php I add this code
public function person()
{
return $this->belongsTo('App\Models\Task');
}
public function getData()
{
return $this->id . ':'/ $this->person->name.')';
}
index.blade.php I add this code
{{ $task2->getData() }}
You can just write a query to get all the task using eloquent to get all the tasks.
$tasksall = Task::all();
Have a look at this link.
Also for you case I think the problem is you are getting task from the User model so you $task will contain only task related to that particular user as you have a belongsTo relation of task with user.
For Your case to get name of User from task.
//Task model
class Task {
public function user()
{
return $this->belongsTo(User::class, 'user_id');
}
}
Then you can query like this in your controller.
$task = Task::find($id);
$name = $task->user->name;

Eloquent: Two foreign keys in a table pointing to the same table

I have two models:
Team
Game (Played between two games)
The Game model has two foreign keys pointing to the Team model - team1_id & team2_id.
Here's the code for Team model:
class Team extends Eloquent
{
protected $table = 'team';
protected $fillable = [
'name',
'color',
'year'
];
public function games()
{
return $this->hasMany(\App\Models\Game::class);
}
}
Code for Game model:
class Game extends Eloquent
{
protected $table = 'game';
protected $casts = [
'team1_id' => 'int',
'team2_id' => 'int'
];
protected $fillable = [
'team1_id',
'team2_id',
'location',
'start_at'
];
public function team1()
{
return $this->hasOne(\App\Models\Team::class, 'team1_id');
}
public function team2()
{
return $this->hasOne(\App\Models\Team::class, 'team2_id');
}
}
I get an error saying the column could not be found.
return $this->hasMany(\App\Models\Game::class, 'team1_id');
This works, but the problem is that I want to get games depending on both team1_id and team2_id.
You had to specify the foreign key and the local key you use to reference that relation
public function localTeam()
{
return $this->belongsTo(\App\Models\Team::class, 'id', 'team1_id');
}
public function foreignTeam()
{
return $this->belongsTo(\App\Models\Team::class, 'id', 'team2_id');
}

Laravel: Querying and accessing child objects in nested relationship with where clauses Many to Many relationship

Hi I have a problem to make my laravel query
Model Regions
class Region extends Model
{
protected $table = 'regions';
protected $guarded = [
];
public function county()
{
return $this->belongsTo('App\Models\County');
}
public function companies()
{
return $this->belongsToMany('App\Models\CompanyInfo',
'region_company','region_id','company_id');
}
Model Category
class Category extends Model
{
protected $table = 'categories';
protected $guarded = [];
public function companies()
{
return $this->belongsToMany('App\Models\Company', 'categories_company','category_id','company_id');
}
}
Model Company
class Company extends Model
{
protected $table ='companies';
protected $guarded = [
];
public function regions()
{
return $this->belongsToMany('App\Models\Region', 'region_company','company_id','region_id');
}
}
I have a input form where I want to filter by category and region and the output should be categories with companies if possible but I want to show only 10 companies per category. Not sure it is is possible.
here is what I have till now
$categories = $request->input('categories');
$region = $request->input('regions');
$companies = Category::whereIn('id',$categories)->with([
'companies.regions' => function ($query) use ($region) {
$query->whereIn('id', $region);
}])->get();
the problem here is that it is filtering the categories but it is still giving me all companies not filtering out the once that belong to certain region.
thank you
Dany
Try the following:
$categories = $request->input('categories');
$region = $request->input('regions');
$companies = Category::whereIn('id',$categories)->with([
'companies' => function ($query) use ($region) {
$query->whereHas('region', function ($q2) use ($region){
$q2->whereIn('id', $region);
});
$query->with(['region']);
$query->limit(10);
}])->whereHas('companies', function($q) use ($region) {
$q->whereHas('region', function ($q2) use ($region){
$q2->whereIn('id', $region);
});
})->get();

Laravel save many-to-many relationship in Eloquent mutators

I've got 2 models with a many-to-many relationship. I want to be able to set a specific attribute with an array of ids and make the relationship in the mutator like this:
<?php
class Profile extends Eloquent {
protected $fillable = [ 'name', 'photo', 'tags' ];
protected $appends = [ 'tags' ];
public function getTagsAttribute()
{
$tag_ids = [];
$tags = $this->tags()->get([ 'tag_id' ]);
foreach ($tags as $tag) {
$tag_ids[] = $tag->tag_id;
}
return $tag_ids;
}
public function setTagsAttribute($tag_ids)
{
foreach ($tag_ids as $tag_id) {
$this->tags()->attach($tag_id);
}
}
public function tags()
{
return $this->belongsToMany('Tag');
}
}
<?php
class Tag extends Eloquent {
protected $fillable = [ 'title' ];
protected $appends = [ 'profiles' ];
public function getProfilesAttribute()
{
$profile_ids = [];
$profiles = $this->profiles()->get([ 'profile_id' ]);
foreach ($profiles as $profile) {
$profile_ids[] = $profile->profile_id;
}
return $profile_ids;
}
public function profiles()
{
return $this->belongsToMany('Profile');
}
}
However the setTagsAttribute function isn't working as expected. I'm getting the following error: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'profile_id' cannot be null (SQL: insert intoprofile_tag(profile_id,tag_id) values (?, ?)) (Bindings: array ( 0 => NULL, 1 => 1, ))
You can't attach many-to-many relations until you've saved the model. Call save() on the model before setting $model->tags and you should be OK. The reason for this is that the model needs to have an ID that Laravel can put in the pivot table, which needs the ID of both models.
It looks like you're calling the function incorrectly or from an uninitialized model. The error says that profile_id is NULL. So if you're calling the function as $profile->setTagsAttribute() you need to make sure that $profile is initialized in the database with an ID.
$profile = new Profile;
//will fail because $profile->id is NULL
//INSERT: profile->save() or Profile::Create();
$profile->setTagsAttribute(array(1,2,3));
Additionally, you can pass an array to the attach function to attach multiple models at once, like so:
$this->tags()->attach($tag_ids);
You can also pass it the model instead of the ID (but pretty sure array of models won't work)
Try using the sync method:
class Profile extends Eloquent {
protected $fillable = [ 'name', 'photo', 'tags' ];
protected $appends = [ 'tags' ];
public function getTagsAttribute()
{
return $this->tags()->lists('tag_id');
}
public function setTagsAttribute($tag_ids)
{
$this->tags()->sync($tagIds, false);
// false tells sync not to remove tags whose id's you don't pass.
// remove it all together if that is desired.
}
public function tags()
{
return $this->belongsToMany('Tag');
}
}
Don't access the tags through the tags() function, rather use the tags property. Use the function name if you want to pop additional parameters onto the relationship query and the property if you just want to grab the tags. tags() works in your getter because you're using get() on the end.
public function setTagsAttribute($tagIds)
{
foreach ($tagIds as $tagId)
{
$this->tags->attach($tagId);
}
}

How to use the `where` method along with the `with` method in laravel 4?

Given the following, very simple, example:
Country Class
class Country extends Eloquent {
protected $table = "countries";
protected $fillable = array(
'id',
'name'
);
public function state() {
return $this->hasMany('State', 'country_id');
}
}
State Class
class State extends Eloquent {
protected $table = "states";
protected $fillable = array(
'id',
'name',
'country_id' #foreign
);
public function country() {
return $this->belongsTo('Country', 'country_id');
}
}
How can I list all the states, based on the id or the name of the country.
Example:
State::with('country')->where('country.id', '=', 1)->get()
The above returns an area, as country is not part of the query (Eloquent must attach the join later, after the where clause).
I think you're either misunderstanding the relations or over-complicating this.
class Country extends Eloquent {
public function states() {
return $this->hasMany('State', 'state_id');
}
}
$country = Country::find(1);
$states = $country->states()->get();

Categories