I could not find this in the laravel docs on aggregate relationships
I was able to do something like this
private function refreshUsers()
{
$this->users = User::withSum(['taskTimeSessions'=> function ($query) {
$query->whereMonth('created_at',$this->month)
->where('is_reconciled',1);
}],'session_duration_in_seconds')
->get();
}
But now I am trying to query what is the total time a Sprint has or at the very least what the individual tasks inside a sprint have so that I can just sum the total of those somehow.
Sprint has many SprintTasks (pivot table)
SprintTask belongs to one Task
Task has many TaskTimeSessions
So I am trying to go find the total time of the TaskTimeSessions
Sprint::with([
'sprintTasks.task'=> function ($query) {
$query->withSum('taskTimeSessions','session_duration_in_seconds');
}])
->get();
I am not getting any errors, but not finding the result anywhere when dd
I thought i would get lucky and have something like this work
->withSum('sprintTasks.task.taskTimeSessions', 'session_duration_in_seconds')
But I am getting this error
Call to undefined method App\Models\Sprint::sprintTasks.task()
If anyone can help me out with some guidance on how to go about this, even if it doesn't include withSum it would be much appreciated.
As requested, these are the models.
// Sprint
public function sprintTasks()
{
return $this->hasMany(SprintTask::class, 'sprint_id');
}
// SprintTask
protected $fillable = [
'sprint_id',
'task_id',
'is_completed'
];
public function task()
{
return $this->belongsTo(Task::class,'task_id');
}
public function sprint()
{
return $this->belongsTo(Task::class,'sprint_id');
}
// Task
public function taskTimeSessions()
{
return $this->hasMany(TaskTimeSession::class, 'task_id');
}
// TaskTimeSessions
protected $fillable = [
'task_id',
'session_duration_in_seconds'
];
public function task()
{
return $this->belongsTo(Task::class,'task_id');
}
Is it possible to abstract this into the model as like
public function totalTaskTime() {
// using the relationship stuff to figure out the math and return it?
}
Looking for any advice on what the best approach is to do this.
Right now I am literally doing this in the blade and seems very bad
#php
$timeTracked = 0;
foreach ($sprint->sprintTasks as $sprintTask) {
$timeTracked += $sprintTask->task->time_tracked_in_seconds;
}
#endphp
You have a many to many relation between sprint and task
For that you can setup a direct relation belongsToMany with sprint_tasks as the pivot table
// Sprint
public function sprintTasks()
{
return $this->hasMany(SprintTask::class, 'sprint_id');
}
public function tasks()
{
return $this->belongsToMany(Task::class, 'sprint_tasks', 'sprint_id', 'task_id')->withPivot('is_completed');
}
Now you can use that relation to query your needs
Sprint::with(['tasks'=> function ($query) {
$query->withSum('taskTimeSessions','session_duration_in_seconds');
}])
->get();
There is a good package for Laravel for complex relationships - eloquent-has-many-deep. You can use it to build relationships through an unlimited number of tables.
composer require staudenmeir/eloquent-has-many-deep:"^1.7"
Sprint.php
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Staudenmeir\EloquentHasManyDeep\HasManyDeep;
use Staudenmeir\EloquentHasManyDeep\HasRelationships;
class Sprint extends Model
{
use HasRelationships;
public function tasks(): BelongsToMany
{
return $this->belongsToMany(Task::class, 'sprint_tasks');
}
public function taskTimeSessions(): HasManyDeep
{
return $this->hasManyDeepFromRelations($this->tasks(), (new Task())->taskTimeSessions());
}
}
Task.php
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Task extends Model
{
use HasFactory;
public function taskTimeSessions(): HasMany
{
return $this->hasMany(TaskTimeSession::class);
}
}
Result:
$sprints = Sprint::withSum('taskTimeSessions', 'session_duration_in_seconds')->get();
Related
i trying to load all rows from a model without the relationship.
The attributes $with it not event set on my Event model but when i do
$events = Event::all();
all my relationship are loaded, and i can see all the query with the dbquerylog.
i don't understand why theses relationship are loaded,
Please help me !
Thanks you.
I'm using Laravel 8.
here's an example.
class Event extends Model {
public function items() {
return $this->hasMany(Item::class);
}
public function items2() {
return $this->hasMany(Item2::class);
}
public function items3() {
return $this->hasMany(Item3::class);
}
public function items4() {
return $this->hasOne(Item4::class);
}
}
$events = Event::all();
If you have a single instance of a model object, you can do:
$obj->withoutRelations();
As laravel documentations says you can use without: https://laravel.com/docs/8.x/eloquent-relationships
Model
protected $with = ['item1','item2','item3','item4'];
Controller
$events = Event::without(['item1','item2','item3','item4'])->get();
I met this problem one day, and it turned out that I was using relation in scope method. Because of this relation values were added to response.
Check out this example:
class Event extends Model {
public function items() {
return $this->hasMany(Item::class);
}
[...]
public function scopeItemsGreen() {
return $this->items->every(function ($item) {
return $item->color == 'green';
});
}
I have the following model:
class EmailAddress extends Model
{
public function scopePrimary($query)
{
return $query->firstWhere('is_primary', true);
}
}
class User extends Model
{
public function emailAddresses()
{
return $this->hasMany(EmailAddress::class);
}
}
echo $user->emailAddresses()->primary()->get();
I would expect Laravel to return a model since firstWhere() essentially does LIMIT 1 in the query but instead I always get a collection with one model. Am I doing something wrong? How to fix that?
Thanks in advance!
Maybe you can utilize the hasOne of many relationship:
class User extends Model
{
public function primaryEmailAddress()
{
$this->hasOne(EmailAddresses::class)->ofMany([], function ($query) {
$query->where('is_primary', true);
});
}
}
I have been trying to figure out how to implement a matching system but have become stuck.
I've managed to build a query within a controller which does exactly the same thing as I want it to do, but I would like to convert it to an Eloquent Model since images are broken and also can access some functions inside my Model.
Here's the query builder within the controller that I wish to convert (if it's possible at all)- I am checking if users have both "liked" each other (similar to Tinder):
class MatchedEmployersController extends Controller
{
public function index()
{
$matches = DB::table('applicant_likes')
->join('employers', 'employers.id', '=', 'applicant_likes.liked_employer_id')
->whereExists(function ($query) {
$query->select(DB::raw(1))
->from('employer_likes')
->whereRaw('employer_likes.employer_id = applicant_likes.liked_employer_id');
})
->get();
return view('applicant.employers.matched', compact('matches'));
}
}
Here's the Applicant model where below I extracted the logic into a usable Traits
App\Models\Applicant
class Applicant extends Authenticatable
{
use Notifiable, LikeableEmployer, MatchableEmployer;
//
public function getAvatarAttribute($value)
{
return asset($value ?: '/images/default-avatar.jpeg');
}
}
App\Trais\LikeableEmployer
trait LikeableEmployer
{
public function likeEmployer(Employer $employer)
{
return $this->likedEmployers()->save($employer);
}
public function unlikeEmployer(Employer $employer)
{
return $this->likedEmployers()->detach($employer);
}
public function toggleLikeEmployer(Employer $employer)
{
if ($this->likingEmployer($employer)) {
return $this->unlikeEmployer($employer);
}
return $this->likeEmployer($employer);
}
public function likingEmployer(Employer $employer)
{
return $this->likedEmployers()->where('liked_employer_id', $employer->id)->exists();
}
public function likedEmployers()
{
return $this->belongsToMany(Employer::class, 'applicant_likes', 'applicant_id', 'liked_employer_id');
}
}
finally, here's where the matched logic should be placed
namespace App\Traits;
use App\Traits\LikeableApplicant;
use App\Traits\LikeableEmployer;
trait MatchableEmployer
{
use LikeableApplicant, LikeableEmployer;
public function matchedEmployers()
{
//
}
}
You need to create a table where you will store the matches. Let's take the following example.
relationships table: id | from | to, It's a match if we have a pair. Example:
id | from | to
1 | 1 | 2
2 | 2 | 1
Now create Relationship Model
class Relationship extends Model
{
public static function getMatch($user_id)
{
return self::leftJoin('relationship reverse', 'relationship.to', '=', 'reverse.from')->where('relationship.from', 'reverse.to')->where('relationship.from', $user_id)->get();
}
}
Now you can simply call User::getMatch('any_user_id');
First of all, create the model for every table you used in this query, then add the following relationship.
In ApplicantLike Model
public function employer(){
return $this->belongsTo('App\Employer','liked_employer_id','id');
}
In Employer Model
public function likes(){
return $this->hasMany('App\EmployerLike','employer_id','id');
}
Final in your MatchedEmployersController
public function index()
{
$matches = ApplicantLike::with('employer','employer.likes')
->has('employer')
->has('employer.likes')
->get();
// dd($matches); // try with this first
return view('applicant.employers.matched', compact('matches'));
}
Try the above code, I converted your given code into ORM, but I think that you are implementing the wrong logic for what you need. If anything will not work fine just reply to me I will help you.
I have Clients, which have Users, which have Surveys with a many-to-many table. So user_surveys.
I'm wondering how I can count some relations deep. I would like to the count of all surveys the users have for that client
What I've tried
Client.php
public function countSurveys()
{
$employees = $this->employees;
// this returns Property [surveys] does not exist on this collection instance.
return $employees->surveys->count();
// Method whereHas does not exist
return $employees->whereHas('surveys')->count();
}
This my employees method, which is a subset of Users
public function employees()
{
return $this->users()->whereHas('roles', function ($q) {
$q->where('name', 'employee');
});
}
And this is the User model
namespace App\Models;
use App\LoginToken;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Spatie\Permission\Traits\HasRoles;
class User extends Authenticatable
{
public function surveys()
{
return $this->belongsToMany(Survey::class, 'user_surveys', 'user_id', 'survey_id')
->withPivot('completed_on', 'status')
->withTimestamps();
}
public function journey()
{
return $this->belongsTo(Scan::class);
}
public function client()
{
return $this->belongsTo(Client::class);
}
}
It might be late, I might be confused and/or stupid.
Looking forward to your responses!
Another approach would be
$user = App\User::find(1);
return $user->surveys()->count();
Or try
$users = App\User::withCount('surveys')->get();
foreach($users as $user) {
$user->surveys_count;
}
Try this:
return $this->employees()->withCount('surveys')->get();
There is no native relationship for this case.
I created a HasManyThrough relationship with support for BelongsToMany: Repository on GitHub
After the installation, you can use it like this:
class Client extends Model {
use \Staudenmeir\EloquentHasManyDeep\HasRelationships;
public function surveys() {
return $this->hasManyDeep(Survey::class, [User::class, 'user_surveys']);
}
}
$count = $client->surveys()->count();
I have the following models.
class User extends Eloquent {
public function comments() {
return $this->hasMany('Comment');
}
}
class Comment extends Eloquent {
public function user() {
return $this->belongsTo('User');
}
}
For the sake of this example, a user could have 1,000s of comments. I am trying to limit them to just the first 10. I have tried doing it in the User model via
class User extends Eloquent {
public function comments() {
return $this->hasMany('Comment')->take(10);
}
}
and via UserController via closures
$users = User::where('post_id', $post_id)->with([
'comments' => function($q) {
$q->take(10);
}
]);
Both methods seem to only work on the first record of the result. Is there a better way to handle this?