Querying model based on complex relations - laravel

I am very new to Laravel and Eloquent and I am trying to define a data model a fetch records from it.
I have following tables
user
task
user_id
tasks_state
user_id
task_id
state
Basically each user can create multiple tasks and each task belongs to exactly one user. Then each task can have multiple task states for each user that interacted with it. (Like task viewed (by other user) or similarly)
I defined the Models as following:
User Model:
class User extends Model {
public function tasksState(){
return $this->hasMany('App\TaskState');
}
public function tasks(){
return $this->hasMany('App\Task');
}
}
Task Model
class Task extends Model {
public function user(){
return $this->belongsTo('App\User');
}
public function tasksState(){
return $this->hasMany('App\TaskState');
}
}
Task State Model
class TaskState extends Model {
public function user(){
return $this->belongsTo('App\User');
}
public function task(){
return $this->belongsTo('App\Task');
}
}
Then when I insert a task state, I do it as following: (Is this correct?)
$task_viewed = new TaskState();
$task_viewed->state = $state;
$task_viewed->user_id = $user->id;
$task->tasksState()->save($task_viewed);
Now, I am facing an issue, how to fetch all tasks for a particular user that were not created by him and he has not interacted with them - either no record in tasks_state table or the task state == 0.
I am also thinking that maybe the relations between models are not correct?

You can do that by using whereDoesntHave() method:
$tasks = Task::where('user_id', '<>', $userId)
->whereDoesntHave('tasksState', function($q) use($userId) {
$q->where('user_id', $userId)->where('state', 0);
})->get();

Related

laravel Eager Load and limit

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.

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 5 - Defining Two Relationships Between Models

I'm learning Laravel, and really OOP in general. I've followed several YouTube tutorial series, that teach you to create a blog in Laravel.
I'm building a task app for a brewery, and I'm trying to define the relationships between the users and the tasks. So I have two models: User.php and Task.php. I had no problem defining the user in a hasMany tasks relationship, and reciprocally, a task belongsTo a user. Where I'm confused is that I'd like to also have a user belong to the task as well. I have two MySQL columns, one with the heading of "user_id" and the other with "user_assigned_id". What I want is that a user has many tasks, but a task also has one assigned user, the idea being that the user that created the task might assign the task to another user. I've found several tutorials on creating relationships between three models, such as a user owning several messages, but only having one address, so I figured that I could just treat two models as if they were three models and connected the User model back to the Task model in a hasOne relationship, but I'm having a really hard time passing that through to the Controller and View.
Here is the relevant code in each file:
User.php
public function tasks()
{
return $this->hasMany('App\Task');
}
Task.php
public function user()
{
return $this->belongsTo('App\User');
}
// Added an user_assigned_id relationship
public function user_assigned()
{
return $this->hasOne('App\User', 'name', 'user_assigned_id');
}
DashboardController.php
public function index()
{
$user_id = auth()->user()->id;
$now = Carbon::now();
$tasks_assigned = Task::orderBy('date', 'asc')->whereDate('date', '>=', $now)->where('user_assigned_id', '=', $user_id)->user_assigned()->where('name', '=', 1)->get();
$tasks_created = Task::orderBy('date', 'asc')->whereDate('date', '>=', $now)->where('user_id', '=', $user_id)->get();
return view('dashboard')->with('tasks_assigned', $tasks_assigned)->with('tasks_created', $tasks_created);
}
I've gotten a bit turned around in the Controller, so I'm not sure if I messed something up there. Basically, I'm getting results from tasks owned by the logged in user, but not assigned to the logged in user.
You can just add a second relationship defined on your Task.php Model and assign a different agent based on user_assigned_id. You can manipulate it as expected via Eloquent.
Task.php
public function user() {
return $this->belongsTo('App\User');
}
public function assignedUser() {
return $this->belongsTo('App\User', 'user_assigned_id');
}
Then on DashboardController.php
$tasks_assigned = Task::orderBy('date', 'asc')->whereDate('date', '>=', $now)->where('user_assigned_id', '=', $user_id)->get();
should work
public function user()
{
return $this->belongsTo('App\User');
}
// Added an user_assigned_id relationship
public function assignee()
{
return $this->belongsTo('App\User', 'user_assigned_id');
}
The relationship is still a belongsTo, you just need to provide the column where the foreign key is held.
Other files:
User.php
public function ownedTasks()
{
return $this->hasMany('App\Task');
}
public function assignedTasks()
{
return $this->hasMany('App\Task', 'user_assigned_id');
}
Dashboard Controller
public function index()
{
$now = Carbon::now();
$tasks_assigned = Auth::user()->assignedTasks()->where('date', '>=', $now)->get();
$tasks_created = Auth::user()->ownedTasks()->where('date', '>=', $now)->get();
return view('dashboard')->with(compact('tasks_assigned', 'tasks_created'));
}

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');
}

Resources