In Laravel Eloquent, select "whereIn" from parent table - php

In my Laravel project (with MySQL database), I have a few models: Time Entries, Tasks, and Projects.
Time Entries belong to Tasks
Tasks belong to Projects
so each table contains a column for the corresponding ID of its parent.
I have an array of Project IDs, and I am trying to select the time entries which, through their tasks, belong to those projects.
In other words, I'd like to be able to do something like this:
$timeEntries = TimeEntry::whereIn('project_id',$projectIds)->get();
But obviously, I get a column not found error, because all I've got in the time entries table is task_id rather than project_id.
Is there a way to select the desired time entries (based on the project IDs I have) in a single Eloquent query? Help much appreciated.

Add the following method in your Project model
public function timeEntries()
{
return $this->hasManyThrough('App\TimeEntry' , 'App\Task');
}
now you can get all time entries of a project like below
$project = Project::find(id);
$project->timeEntries()->get();

So the type of relation you're explaining is a through relation (http://laravel.com/docs/5.1/eloquent-relationships#has-many-through).
Instead of trying to head up the tree head down the tree from projects->tasks->time_entries.
Projects::whereIn($projectIds)->with('time_entries')->get();
The resulting collection will be projects with (should be at least) a field called time_entries in each project that have all the relevant times.

Related

Laravel - Update models relations to another model

I have a many-to-many relationship between an incident model and a Patient model. An incident can have many patients and a patient can be involved in many incidents.
Should it occur that a user creates duplicates of a patient model we want to be able to merge those two patient models into one. This means that I want to move the incidents that patient 1 is involved in to patient 2 including additional attributes that are sitting on the pivot table.
I've tried something as simple as
Casualty::where('patient_id', $patientOne->getKey())->update(['patient_id' => $patientTwo->getKey()]);
But this doesn't work. Using the updateOnExistingPivot() method would mean I need to iterate over every incident for patient 1 and run a separate DB query to update the patient to patient 2.
I've also tried updating the record like this
$patientOne->incidents()->update(['patient_id' => $patientTwo->getKey()]);
This also doesn't work because there is no patient_id column on the incidents table.
How can I achieve this or am I doing something wrong?
Not sure if I understood you, you want to group more patients into the same accident? You could go with the belongsToMany relation and make one pivot table. Then, when you want to update the data simply use the sync method.
You can also try storing them with json_encode() in one column which will hold only users ID's and later on retrieve them.
Sorry, can't give more info since the question is not described that well.

Laravel select from DB where in Collection - Laravel Nova

I have a DB, "views," with many, many entries. I also have a "Courses" table, which these views are one-many related to. In Laravel Nova, I can get a metric of all views over time for a course with some code like this:
public function calculate(Request $request)
{
return $this->countByDays($request, view::where('viewable_id', $request->resourceId));
}
In this case, viewable_id is the id of the course, and $request->resourceId gives the ID of the course to sort by. Pretty simple.
However, now things get a little difficult. I have another model called Teachers. Each Teacher can have many courses, also in a one-many relationship. How do I get a metric of views over time for all the courses that teacher teaches?
I assumed the simplest way to do this would be to create a Laravel Collection with all courses the Teacher teaches (not exactly efficient), and then select all views in the database where viewable_id matches one of the courses in that list. Of course, by posting this, I couldn't figure out how to do that.
Of course, once this is figured out, I'd love to do the same thing for Categories (though that should function in a very identical manner to Teachers, so I don't need to ask that question).
How do I get a metric of views over time for all the courses that teacher teaches?
This should be the "countByDays" of views where the viewable_id is in the list of course ids that the teacher teaches.
An SQL query statement to achieve that is given below:
select * from "views"
where "viewable_id" in (select "id" from "courses" where "teacher_id" = ?)
The Eloquent query should be similar to:
$this->countByDays($request,
view::whereIn(
'viewable_id',
Course::with('teacher')
->select('id')
->where('teacher_id', $request->resourceId)
)
);

Using special union functions inside Laravel Eloquent model

I've started developing a website using Laravel, and im pretty much finding everything through the official documentation and answers that I find here. However there is 1 thing that -even though i've found a way to do-, I have a feeling that could be done in another, more optimized way than the one I'm doing it right now. Let me explain.
For my website, I have a table called "players", which has data about some players extracted from a football game. Let's say the structure is like this:
ID (int, primary key, A_I)
GameID (int, unique) //what the game uses
PlayerName
Data (basically many different columns)
Since the purpose of my website is to allow users to do modifications on the game content, I also have another another table that I call "userplayers", which I use for doing modifications on the players that exist on the original table, or for adding new ones. The structure is like the "players" table, however with just one column added, called userID, which is to identify which modification belongs to which user.
ID (int, primary key, A_I)
GameID (int, unique together with userID)
PlayerName
Data (basically many different columns)
UserID (int, unique together with GameID)
If I add an entry on the userplayers table, if that entry has the same GameID as any entry on the players table (and the user that has created it is logged in), then on runtime it overwrites the players' table entry. If the GameID of the new entry doesnt exist on the original players table, then it just gets appended to it.
If the user is not logged in, then I just return the players table.
By using eloquent laravel model I can easily retrieve the players table for when the user is not logged in. However, I can't figure out an efficient way to return the whole DB + the user created content with just using core eloquent model functions.
In the past (without Laravel) I was using this DB query:
SELECT * FROM (SELECT *, NULL FROM players WHERE NOT EXISTS (SELECT * FROM userplayers WHERE players.gameid=userplayers.gameid AND userId=$userId) UNION (SELECT * FROM userplayers WHERE userId=$userId)) AS pl;
And the way I've "found" to do something like this in Laravel is by adding a local scope inside the Players model like this:
public function scopeIncludeCustom($query, $user)
{
return \DB::select('SELECT * FROM (SELECT *, NULL FROM players WHERE NOT EXISTS (SELECT * FROM userplayers WHERE userplayers.gameid=players.gameid AND userId='.$user.') UNION (SELECT * FROM userplayers WHERE userId='.$user.')) AS players_full);
}
However you can understand that this doesn't return the $query as intended by scopes, but just a php array, which im returning back, and I think that's not the correct way to do this. For example, when I'm just searching the players table (without using user created content), it takes a much much shorter time to return results than returning results with the custom content.
Any ideas are hugely appreciated.
So, after some days of researching my issue and possible solutions, I came up with this solution.
So, lets take this step by step.
I had a "Player" model, that fetched data from the "players" table.
I also had a "Userplayer" model, that fetched data from the "userplayers" table.
I had to create a relation between those 2 models. An entry in the "players" table may have many entries related to them in the "userplayers" table. So, in the Player Model I added this:
public function userplayers()
{
return $this->hasMany('App\Userplayer', 'gameid', 'gameid');
}
While in the Userplayer Model, I added this:
public function player()
{
return $this->belongsTo('App\Player', 'gameid', 'gameid');
}
When requesting data, the first step was to remove every row from the "players" table that had the same "GameId" as any row returned from the "userplayers" table, which also had a restriction that the "userId" in every row of this table must be a specific one.
This is the code that does this in my SearchPlayerController:
$orig_players = \App\Player::whereDoesntHave('userplayers', function ($query) use($user)
{
$query->where('userId', $user);
})->get();
At the same time, I need to get every row from the "userplayers" table that has the "userid" I want
$userplayers = \App\Userplayer::where([
['pesid', $search]
])->get();
Now, I just merge the 2 results (I can't use UNION because the data I fetch from the "players" table has one less column).
$players = $orig_players->merge($userplayers)
->sortBy('registeredName')
->take($limit);
And everything works perfectly fine, and a lot faster than before!

Chaining many to many pivot table query Laravel

In my application I have a search filter for Jobs (Model Job) which have a many to many relation with Services (Model Service). Apart from this Jobs have other characteristics as well presented as columns inside the jobs table as status for example.
So what I am doing is I have a multiple input form which data after submit I transform to query my database.
What I am doing is:
$job = new Job();
and then start my query with
$job->newQuery();
and start chaining wheres depending on the data from the form like
$job->newQuery()->where('status', 2);
etc.
My problem comes when the time to chain the services as a where clause. In the form the services is represented as a multiple select box so the data available to me to chain in a where clause is an array like [1,2,5] which represent the ids of the services the jobs I am looking for include but I do not know how to do this in Laravel.
I do have a pivot table job_service which has job_id and service_id etc. Any help would be appreciated thanks in advance.
What I have until now in the case I am searching for jobs that have the services [1,3,6] is something like
App\Job::where('status', '=', 2)
->whereHas('services', function($q) {
$q->whereIn('service_id',[1,3,6]);
}, '=',3)->get();
But the above returns results which have more than 3 services as well:(

Adding a custom query to the model

For simplicity's sake, I have two tables: projects and tasks. A project can have many tasks, and a task belongs to a project.
I've set up the database, created the associations and used cake bake to generate the models, controllers and views and all is perfect.
When I look at a the index view for projects, I see a table listing all the projects as expected. What I want to do is really simple: I want a column in that table that shows a count of all the tasks assigned to that project.
The SQL query for this is trivial (returning a list of project names and a count of the tasks):
SELECT projects.name,
(SELECT COUNT(*) FROM tasks WHERE project_id = projects.id) as taskCount
FROM projects
So how do I achieve this in CakePHP?
The index method in the projects controller looks like this at the moment:
function index() {
$this->Project->recursive = 0;
$this->set('projects', $this->paginate());
}
You can use the virtualFields or counterCache, but it is another way.

Categories