In my website users can create candidates (one-many relation) and those candidates can have skills (many-many).
User model:
public function candidates() {
return $this->hasMany(Candidate::class);
}
candidate model:
public function skills() {
return $this->belongsToMany(Skill::class, 'candidate_skill', 'candidate_id', 'skill_id');
}
Skills model:
public function candidates() {
return $this->belongsToMany(Candidate::class, 'candidate_skill', 'candidate_id', 'skill_id')
->withTimestamps();
}
I already have an index page where an user can view ALL his made candidates
$candidates = Auth::user()->candidates;
on the edit page, skills can get synced to the candidate in question
$candidate->skills()->sync(request()->skills);
and back to the index page it shows how many skills the candidates have
<td>{{count($candidate->skills)}}</td>
now, I need to make a search bar. My table already has one(dataTable) to search in the already loaded td's. But I need a search bar to search for candidates which have certain skills, say I want to search for candidates which are synced with 'css' and only show those in the table.
I read through the laravel docs and tried eager loading it:
$hasCss = $candidates->load(['skills' => function ($query) {
$query->where('name', '=', 'css');
}]);
but this just loaded all candidates with only the css skill displayed, even the candidates who dont have it. I want to only load candidates who have the skill and leave the others out.
How do I do this, I'm clueless :s
You're looking for whereHas(...)
Current user's candidates that have CSS skill:
$withCss = Auth::user()->candidates()->whereHas('skills', function ($q) {
$q->where('name', 'css');
})->get();
In your Skill Model, you have an error in your relationship definition. You wrote
public function candidates() {
return $this->belongsToMany(Candidate::class, 'candidate_skill', 'candidate_id', 'skill_id')
->withTimestamps();
}
Swap 'candidate_id' and 'skill_id'. The local foreign key always comes first.
public function candidates() {
return $this->belongsToMany(Candidate::class, 'candidate_skill', 'skill_id', 'candidate_id')
->withTimestamps();
}
Related
Good day. I have this relationship problem in laravel
I have three models
User
State
School
I have established the following relationships between them
a. School - User (A user can enroll in many schools)
In User Model
public function schools(){
return $this->belongsToMany('App\Models\School', 'school_user', 'user_id', 'school_id');
}
b. A school can have many users
In School Model
public function users(){
return $this->belongsToMany('App\Models\User', 'school_user', 'school_id', 'user_id');
}
c. A school belongs to a state (i.e c an be found in a state)
In School Model
public function states(){
return $this->belongsTo('App\Models\State');
}
d. A state has many schools
In State Model
public function schools(){
return $this->hasMany('App\Models\School');
}
Now, I know how to query models with immediate relationship. For example, I can get the users associated with a school and vice-versa. I can also get the schools in a state.
My Question
How do I get all users associated with schools in a given state (using a query)? Assuming all we have is just the name of a state.
And probably get all schools the users are associated with and the date(time) in which there were associated with the schools?
This is what I have done. The second query is not giving me an answer
public function details(){
//Get all schools associated with a state
$schools = State::with('schools')->where('id',1)->first();
foreach($schools->schools as $data){
//Get all users associated with the schools
$users = School::with('users')->where('id',$data->id);
dd($users);
}
}
Apart from the fact that this approach is not probably right, I am not getting any answer. Is there a query or method that can solve this?
Thanks.
You should see the related models in the relations array attribute
$school= App\Models\School::with('User', 'State')->get();
Using a whereHas you can get all users that belong to schools with the given state_id.
$state = State::find(1);
$users = User::whereHas('schools', function ($query) use ($state) {
$query->where('state_id', $state->id);
})->get();
Querying Relationship Existence
If you need even more power, you may use the whereHas and orWhereHas
methods to define additional query constraints on your has queries,
such as inspecting the content of a comment:
use Illuminate\Database\Eloquent\Builder;
// Retrieve posts with at least one comment containing words like code%...
$posts = Post::whereHas('comments', function (Builder $query) {
$query->where('content', 'like', 'code%');
})->get();
// Retrieve posts with at least ten comments containing words like code%...
$posts = Post::whereHas('comments', function (Builder $query) {
$query->where('content', 'like', 'code%');
}, '>=', 10)->get();
sorry for the title of this question but I am not sure how to ask it...
I am working on a project where I have two Models Trains and Cars, to this model I have a belonging Route.
I want to make a query and check if the routeable_type is App\Car than with the selected routeable_id to get the data from the Car. And if the routeable_type is Train then with the ID to get the data from the Tran.
So my models go like this:
Train:
class Train extends Model
{
public function routes()
{
return $this->morphMany('App\Route', 'routeable');
}
}
Car:
class Car extends Model
{
public function routes()
{
return $this->morphMany('App\Route', 'routeable');
}
}
Route:
class Route extends Model
{
public function routeable()
{
return $this->morphTo();
}
}
And the query I have at the moment is:
$data = Route::leftjoin('cars', 'cars.id', '=', 'routes.routeable_id')
->leftjoin('trains', 'trains.id', '=', 'routes.routeable_id')
->select('routes.id', 'cars.model AS carmodel', 'trains.model AS trainmodel', 'routeable_type', 'routes.created_at');
With this query if I have the same ID in cars and trains I get the data from both and all messes up. How do I check if routeable_type is Car ... do this, if routeable_type is Train .. do that?
Will something like this be possible in a 1 single query:
$data = Route::select('routes.id', 'routeable_type', 'routes.created_at');
if(routeable_type == 'Car'){
$data = $data->leftjoin('cars', 'cars.id', '=', 'routes.routeable_id')->select('routes.id', 'cars.model AS carmodel', 'routeable_type', 'routes.created_at');
}else{
$data = $data->leftjoin('trains', 'trains.id', '=', 'routes.routeable_id')->select('routes.id', 'trains.model AS trainmodel', 'routeable_type', 'routes.created_at');
}
Maybe this is what you are looking for?
DB::table('routes')
->leftJoin('cars', function ($join) {
$join->on('cars.id', '=', 'routes.routeable_id')
->where('routes.routeable_type', 'App\Car');
})
->leftJoin('trains', function ($join) {
$join->on('trains.id', '=', 'routes.routeable_id')
->where('routes.routeable_type', 'App\Train');
})
->select('routes.id', 'cars.model AS car_model', 'trains.model AS train_model', 'routes.routeable_type', 'routes.created_at');
->get();
I think you may want to follow the morphedByMany design.
https://laravel.com/docs/5.7/eloquent-relationships#many-to-many-polymorphic-relations
This was also a neat visual for the different relation types.
https://hackernoon.com/eloquent-relationships-cheat-sheet-5155498c209
I was faced with a similar issue though I failed to follow the correct design initially and was forced to query the many possible relations then wrote custom logic after to collect the relation types and ids then do another query and assign them back through iteration. It was ugly but worked... very similar to how Eloquent does things normally.
i don't have enough repo, so i can't comment. that's why i am putting as an answer.
You should use 2 different queries, for each model.
This will be better, code wise as well as performance wise. also if both models have similar fields you should merge them to 1 table and add a 'type' column.
and put non-similar fields in a 'meta' column.
( in my opinion )
For example: I have these models in my application.
User, Profile, Interest.
I linked the users table with the profiles table by adding the user_id column in the profiles table. And I linked profiles and interests by using a pivot table (interest_profile), Which is (as obvious) will have two columns (profile_id, interest_id).
However, I want to query the users who are associated with a profile, too see who is associated with a particular interest, In other words: "select all users who are having (in their profiles) that particular interest".
I know that I can do this with raw SQL by joining the four tables and then use (where clause).. But I want to do it the Laravel way.
Thanks in advance.
First make sure you have your relationships setup correctly on your models like:
class User extends Model
{
public function profile()
{
return $this->hasOne(Profile::class);
}
}
class Profile extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
public function interests()
{
return $this->belongsToMany(Interest::class, 'interest_profile');
}
}
class Interest extends Model
{
public function profiles()
{
return $this->belongsToMany(Profile::class, 'interest_profile');
}
}
Then you can use whereHas() to constrain a query by a related model and dot notation for nested relations. So your query would be:
User::whereHas('profile.interests', function($query) use ($interestName) {
return $query->where('name', $interestName);
})->get();
That would just return a collection of users. If you wanted to return their profiles and interests as well you would use with():
User::whereHas('profile.interests', function($query) use ($interestName) {
return $query->where('name', $interestName);
})
->with('profile.interests')
->get();
Assuming the User model has a relationship profile and the Profile model has a relationship interests, you can do this.
$interest_id = 1;
$users = User::whereHas('profile', function ($query) use ($interest_id) {
$query->whereHas('interests', function ($query) use ($interest_id) {
$query->where('id', $interest_id);
});
})->get();
I would like to display the posts of everyone the current user follows, ordered by date desc.
I have a many to many relationship supplying all the people the user is following.
$users = User::find(Auth::user()->id)->follow()->get();
I have a one to many relationship displaying the posts for any user.
$updates = App\User::find(?????)->updates()->orderBy('created_at', 'desc')->get();
The question mark's shows where the followers ID's need to be placed.
I can put the above query inside the for each loop but that obviously works its way through each follower rather than all posts in date order.
I suspect I may need to set a new relationship and work from the beginning. Can anyone advise.
User Model
public function updates()
{
return $this->hasMany('App\update');
}
/**
* User following relationship
*/
// Get all users we are following
public function follow()
{
return $this->belongsToMany('App\User', 'user_follows', 'user_id', 'follow_id')->withTimestamps()->withPivot('id');;;
}
// This function allows us to get a list of users following us
public function followers()
{
return $this->belongsToMany('App\User', 'user_follows', 'follow_id', 'user_id')->withTimestamps();;
}
}
Update Model
public function user_update()
{
return $this->belongsTo('App\User');
}
Thank you.
Since you want the posts, it is probably going to be easier starting a query on the Post model, and then filter the posts based on their relationships.
Assuming your Post model has an author relationship to the User that created the post, and the User has a follower relationship to all the Users that are following it, you could do:
$userId = Auth::user()->id;
$posts = \App\Post::whereHas('author.follower', function ($q) use ($userId) {
return $q->where('id', $userId);
})
->latest() // built in helper method for orderBy('created_at', 'desc')
->get();
Now, $posts will be a collection of your Post models that were authored by a user that is being followed by your authenticated user.
My Model Items is related to Rooms, which is related to Buildings, which is related to Locations.
Short: Items belongsTo Rooms belongsTo Buildings belongsTo Locations
At the index function of the ItemController I want to show a table of Items. I use Laravel Datatables. It works for 'simple' tables, but I ran into the problem of sorting/searching custom/appended fields, because they are not in the table of course.
Basically I want to join the Room, Building and Location Name for each Item in the displaying table.
This is my Items Model:
protected $appends = [
'building_title',
'location_title',
'room_title',
];
public function getLocationTitleAttribute() {
return $this->room->building->location->title;
}
public function getBuildingTitleAttribute() {
return $this->room->building->title;
}
public function getRoomTitleAttribute() {
return $this->room->title;
}
This is my Controller:
public function anyData()
{
return Datatables::of(Item::query())->make(true);
}
What is the best approach to enable sorting/filtering for appended fields?
Thank you for reading.
This is my solution (no appended fields were used):
public function anyData()
{
$items = Item::join('rooms', 'items.room_id', '=', 'rooms.id')
->join('buildings', 'rooms.building_id', '=', 'buildings.id')
->join('locations', 'buildings.location_id', '=', 'locations.id')
->select([
'items.id',
'items.title',
'items.label_number',
'items.fibu_number',
'rooms.title as room_title',
'buildings.title as building_title',
'locations.title as location_title'
]);
return Datatables::of($items)->make(true);
}