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
Related
I'm using Laravel Filament.
I got a projects and responsibles tables in a many-to-many relationship. But also another table of responsabilityTypes
projects
id
title
responsibles
id
name
responsabilityTypes
id
name
project_responsible
project_id
responsible_id
responsibilityType_id
And here are my relationships setup:
Responsible.php
public function projects() {
return $this->belongsToMany(Project::class,'rel_project_responsible','responsible_id','project_id')
->withPivot('responsibilityType_id')
->withTimestamps()
->using(AcademicoProyecto::class);
}
Project.php
public function responsibles() {
return $this->belongsToMany(Responsible::class,'rel_project_responsible','project_id','responsible_id')
->withPivot('responsibilityType_id','sort')
->withTimestamps()
->using(AcademicoProyecto::class);
}
I have set up a class for the pivot table like so:
ProjectResponsible.php
use Illuminate\Database\Eloquent\Relations\Pivot;
class AcademicoProyecto extends Pivot
{
}
ResponsibilityType.php
//Don't know how to set up
My question is, when the user is in a Project Edit page and clicks on the "attach" button, in order to add a Responsible record, a Modal pops up to select a Responsible, but I also implemented a Select list to display the different types of responsibilities.
What am I missing to set up in order to access and display the types of responsibilities in the select list and attach it to the pivot table?
Your question asks about "access and display" but you have no controller or view code. But for the model, it's just a simple relationship between two tables, so define it as such:
class AcademicoProyecto extends Pivot
{
use SoftDeletes;
public function responsibilityType() {
return $this->belongsTo(ResponsibilityType::class);
}
}
class ResponsibilityType extends Model
{
protected $fillable = ["name"];
}
Now you simply update the other models to access the relationship in the withPivot() call.
class Responsible extends Model {
public function projects() {
return $this->belongsToMany(Project::class,'rel_project_responsible','responsible_id','project_id')
->withPivot('responsibilityType')
->withTimestamps()
->using(AcademicoProyecto::class);
}
}
class Project extends Model {
public function responsibles() {
return $this->belongsToMany(Responsible::class,'rel_project_responsible','project_id','responsible_id')
->withPivot('responsibilityType', 'sort')
->withTimestamps()
->using(AcademicoProyecto::class);
}
}
Now you should be able to do, for example:
$foo = Responsible::with("projects")->first();
foreach ($foo->projects as $project) {
echo $project->pivot->responsibilityType?->name;
}
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.
I got the following tables
actors
id
name
stats
id
name
actor_stat
actor_id
stat_id
quantity
I want, given an actor's name, take all the quantities associated.
Actor model:
class Actor extends Model
{
public function stats()
{
return $this->belongsToMany('App\Stat', 'actor_stat')->withPivot('quantity');
}
}
Stat model:
class Stat extends Model
{
public function users()
{
return $this->belongsToMany('App\User', 'actor_stat')->withPivot('quantity');
}
}
Query
public function request(){
$actors = Actor::where('name','Jack')->first();
$stat = $actors->pivot->quantity; //??
return response()->json(['actors' => $actors, 'stat' => $stat]);
}
Suggestions?
you can use eager loading to load related table like this:
$actors = Actor::with('stats')->where('name','Jack')->first();
//do loop foreach actor state then you can access to quantity column
foreach($actors->states as $state)
$state->pivot->quantity;
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();
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');
}