laravel Eager Load and limit - laravel

I meet this problem: I have model of people and model of their activities and I want to declare relation like latest activity inside of person model but Laravel does not give permission to use 'Limit' or 'Take' inside of Eager Loads
So how it could be done ?
I tried this inside of person model
public function lastActivity()
{
return $this->belongsToMany('App\Models\Activity','activity_job','job_id','activity_id')
->orderByDesc('created_at')->limit(1);
}
But it takes latest activity of one person not for all 🤯
Please Help

Let's say you have a model Person (or People, whatsoever...)
(Person.php)
class Person extends Model {
protected $table = 'persons';
public function activities()
{
return $this->hasMany(Activity::class);
}
...
}
And a model Activity
(Activity.php)
class Activity extends Model {
...
public function person()
{
return $this->belongsTo(Person::class);
}
...
}
Now to get all Persons with their latest activity
Person::with(['activities' => function ($query) {
$query->orderBy('id', 'desc');
}])->get()
->map(function ($person) {
$person->setRelation('activities', $person->activities->take(1));
return $person;
});
Explanation:
We get all Persons with all of their activities. Then we map through the collection and (re)set the activities relationship with only one item of the activities collection. Because we ordered the activities by id in descending order, take(1) will give us the persons latest activity.

Related

How to Get User's myProjects' Tasks with Eloquent in Laravel

I'm new to Laravel and Eloquent.
In my app, I want to retrieve the current user's and other project members' tasks and show them in the view.
Here's the database relation I have made:
users (id)
projects (id, user_id)
user_project (id, user_id, project_id, role)
tasks (id, user_id, project_id)
(I have defined the required foreign keys in migrations)
To explain the relation, each project has a user (who has created the project). But in the user_project table, users can be assigned to other projects as well and their role is defined there. For example: There are two users:
id=1
id=2
And three projects:
id=1, user_id=1
id=2, user_id=2
id=3, user_id=1
user_project relationship:
id=1, user_id=1, project_id=1, role=admin
id=2, user_id=2, project_id=1, role=employee
id=3, user_id=2, project_id=2, role=admin
id=4, user_id=1, project_id=3, role=admin
And four tasks:
id=1, user_id=1, project_id=1
id=2, user_id=2, project_id=1
id=3, user_id=1, project_id=2
id=4, user_id=1, project_id=3
I want the user id=2 to be able to see tasks of project_id=1 because he is invited to that project as employee together with the tasks of project_id=2 because he has created that project. Of course the user shouldn't be able to see the tasks of project_id=3 because he's not a member. So, what's the neatest way to do this?
Here are the models I have defined:
class User extends Authenticatable
{
public function projects(){
return $this->hasMany(Project::class);
}
public function tasks(){
return $this->hasMany(Task::class);
}
public function joinedProjects()
{
return $this->hasMany(ProjectUser::class);
}
}
class Project extends Model
{
public function tasks(){
return $this->hasMany(Task::class);
}
public function users(){
return $this->hasMany(User::class);
}
}
class ProjectUser extends Model
{
}
class Task extends Model
{
public function projects(){
return $this->belongsTo(Project::class);
}
public function users(){
return $this->belongsTo(User::class);
}
}
Here's the way I'm trying to retrieve tasks of my project members (the ideal way I'm looking for to do this is: $tasks = $user->joinedProjects->tasks but I don't know how to do it, so here's how I'm currently trying to get this done):
class TasksController extends Controller
{
public function index()
{
$user = Auth()->user();
//I guess there are better ways to retrieve projects, but:
$projects = app('App\Http\Controllers\HomeController')->allProjects($user);
foreach($projects as $project){
$tasks[] = Task::where('project_id', $project->id);
}
return view('tasks.index', compact(['tasks','user']));
//gives error: Property [id] does not exist on the Eloquent builder instance
//when I'm trying to get $task->id in a foreach loop.
}
}
And here's the home controller (I need the allProjects() function in HomeController for some other class functions):
class HomeController extends Controller
{
function allProjects($user){
$projects = $user->projects;
$otherProjects = \App\ProjectUser::where('user_id',$user->id)->get();
foreach ($otherProjects as $project){
$projects[] = \App\Project::find($project->project_id);
}
return $projects;
}
}
First of all I think you should setup the joinedProjects as a many-to-many relationship instead, that way accessing it will feel more straightforward.
// in User model
public function joinedProjects()
{
// i'm assuming you want to always have access to the role property
return $this->belongsToMany(Project::class, 'user_project')->withPivot('role');
}
// in Project model
public function memberUsers()
{
return $this->belongsToMany(User::class, 'user_project')->withPivot('role');
}
With this relationship you should be able to call $user->joinedProjects to get the list of project that the user has joined.
To get the task, of course you can call the joinedProjects relationship and loop through the resulting project like the for loop you have set up. Or as an alternative you can make use of the collection class' pluck method.
class TasksController extends Controller
{
public function index()
{
// here I put load to eager load the project and task
$user = Auth()->user()->load('joinedProjects.tasks');
// OPTION 1: you can loop to get the tasks
$tasks = collect();
foreach($user->joinedProjects as $project){
$tasks = $tasks->merge($project->tasks);
}
// OPTION 2: or use pluck (must be eager loaded to work)
$tasks = $user->joinedProjects->pluck('tasks');
// $tasks should be unique here, but if it is not you can call the unique method of collection
$tasks = $tasks->unique('id');
return view('tasks.index', compact(['tasks','user']));
}
}
Your HomeController that you shared can also be simplified with the new relationship
class HomeController extends Controller
{
function allProjects($user){
// here i'm assuming user automatically joins a project when they create it
$projects = $user->joinedProjects;
return $projects;
}
}
Here's some additional reference to the unique method i use in the code and the lazy eager loading

Laravel working with relations that arent related

Consider I have three models: Subscription, Description and Expense.
Each Subscription belongsTo a Description, and each Description hasMany Expenses:
Model Subscription:
public function description()
{
return $this->belongsTo(Description::class);
}
Model Description:
public function expenses()
{
return $this->hasMany(Expense::class);
}
Model Expense:
public function description()
{
return $this->belongsTo(Description::class);
}
I want to be able to retrieve all expenses made for a subscription, based on the Description. When I have a single subscription, I can do that as follows:
$subscription->load(['description.expenses' => function ($q) use (&$transactions, $subscription) {
$transactions = $q->with('description')
->get());
}]);
But when I want to check all expenses that have been done for all subscriptions, I am unsure how to go about it. I've tried using hasManyThrough, but I think that wouldn't work at all, since either my Description nor my Expense have a subscription_id. I also do not want to set up a direct relation for Subscription and Expense.
Any pointers how to fetch all expenses that have been made for all subscriptions? Or rather, the sum of all expenses for all subscriptions?
You could append an attribute on the Subscription model:
<?php
class Subscription extends Model {
protected $appends = ['total_expenses'];
...
public function totalExpenses()
{
return $this->description()
->with(['expenses'])
->get()
->pluck('expenses.amount', 'amount')
->sum('amount');
}
}
// do something...
Subscription::all()->each->total_expenses
I actually could do just the same thing as I already posted:
Subscription::all()->load(['description.expenses' => function ($q) use (&$transactions) {
$transactions = $q->get();
}]);

Laravel Eloquent - Get all records of child relation model

My data model is this:
Users > Offices > Organization
This is my model
class Organization extends Model {
protected $table = 'organizations';
public function offices()
{
return $this->hasMany('App\Models\Office');
}
public function users()
{
return $this->offices()->users();
}
....
So.. I want to get all users from an organization (of all the offices).
But I don't know how to do something like
$this->offices()->users();
(Avoiding user a manual collection or map to do that)
Thanks!
So, you have organization ID. You can load all users by using whereHas():
$users = User::whereHas('office', function ($q) use ($organizationId) {
$q->where('organization_id', $organizationId);
})
->get();
Make sure office() relationship is defined correctly in User model:
public function office()
{
return $this->belongsTo('App\Office');
}
Alternatively, you could define hasManyThrough() relationship:
public function users()
{
return $this->hasManyThrough('App\Office', 'App\User');
}
And use it:
$organization->users()

Laravel Eloquent Relationship Through Tables

I have the following database tables:
users
id
name
seasons
id
name
teams
id
name
standings
id
season_d
team_id
user_id
fixtures
id
season_id
home_team_id
away_team_id
My question is, how would I get which user a team belongs to for a certain fixture? For example I may want to do the following:
$fixture = Fixture::find(1);
echo $fixture->homeTeam->user->name;
My models look like this:
Fixture Model
class Fixture extends Eloquent{
public function season(){
return $this->belongsTo('Season');
}
public function homeTeam(){
return $this->belongsTo('Team', 'home_team_id');
}
public function awayTeam(){
return $this->belongsTo('Team', 'away_team_id');
}
}
Team Model
class Team extends Eloquent{
public function standings(){
return $this->hasMany('Standing');
}
public function seasons(){
return $this->belongsToMany('Season', 'Standings');
}
public function users(){
return $this->belongsToMany('User', 'Standings');
}
}
Season Model
class Season extends Eloquent{
public function standings(){
return $this->hasMany('Standing');
}
public function teams(){
return $this->belongsToMany('Team', 'Standings');
}
public function users(){
return $this->belongsToMany('User', 'Standings');
}
public function fixtures(){
return $this->hasMany('Fixture');
}
}
I think I need to add a user function to the Team model instead of the current users function that's there, but I can't figure out the correct way to do the relationship. A team will only have one user for any given season. Any help would be appreciated, thanks!
UPDATE
I have added the following relationships to the Fixture model, which allows me to get the user and team through the standings table:
public function homeTeamStanding(){
return $this->belongsTo('App\Modules\Leagues\Models\Standing', 'home_team_id', 'team_id')->where('season_id', $this->season_id);
}
public function awayTeamStanding(){
return $this->belongsTo('App\Modules\Leagues\Models\Standing', 'away_team_id', 'team_id')->where('season_id', $this->season_id);
}
The problem with this is that I can't use it with eager loading, so there's quite a lot of queries running, as when I try to eager load them $this->season_id is null. Surely there's a better way?
I replaced the above with joins instead so that I can eager load which results in a lot less queries!
public function homeTeamStanding(){
return $this->belongsTo('App\Modules\Leagues\Models\Standing', 'home_team_id', 'team_id')
->join('fixtures', 'fixtures.season_id', '=', 'standings.season_id');
}
public function awayTeamStanding(){
return $this->belongsTo('App\Modules\Leagues\Models\Standing', 'away_team_id', 'team_id')
->join('fixtures', 'fixtures.season_id', '=', 'standings.season_id');
}

Cannot access Collection::$items

I've got some troubles with an eloquent query.
Users have many feeds and feeds have many items.
I need to get all the items that belongs to the feeds of the user order by date.
I've got a pivot table:
feed_user
----------
- id
- feed_id
- user_id
and relationships are defined like this in my models:
class UsersController extends BaseController {
public function feeds() {
return $this->hasMany('feed');
}
class Feed extends \Eloquent {
protected $fillable = [];
public function users() {
return $this->belongsToMany('User');
}
public function items() {
return $this->hasMany('Item');
}
class Item extends \Eloquent {
protected $fillable = [];
public function feed() {
return $this->belongsTo('Feed');
}
But when I do this query...
Auth::user()->feeds->items->orderBy('date', 'DESC')->get();
It returns this error:
Cannot access protected property Illuminate\Database\Eloquent\Collection::$items
There are a couple issues here.
First, the relationship on User model is not correct. A hasMany relationship is one half a one-to-many relationship. This would assume that a feed belongs to one user, and that the feed table has the user_id field. A many-to-many relationship is defined by adding a belongsToMany relationship on both models. So, a user belongsToMany feeds, and a feed belongsToMany users.
class User extends \Eloquent {
public function feeds() {
return $this->belongsToMany('feed');
}
}
Next, the error you're seeing is because Auth::user()->feeds returns a Illuminate\Database\Eloquent\Collection object. You're then trying to access the items attribute on the Collection, which is protected and throws the error you're seeing.
Finally, since Laravel does not use joins for relationships, you cannot order a query by a field on a related table without manually doing the join yourself.
Try using eager loading:
Auth::user()->with('feeds.items')->orderBy('date', 'DESC')->get();

Resources