Laravel retrieve multiple relationships - laravel

I have a laravel project where a User can have many Client class. The Client can have many Session and and a Session can have many Assessment and many Plan. I am using hasManyThrough on the Client to get Assessment and Plan. Each Assessment and Plan has a review_date timestamp saved into the database.
What I'd like to do is get all the Assessment and Plan for any Client with their review_date as today. Ideally something like:
$events = Auth::user()->reviews()->today();
What I don't know how to do it make the reviews function, because it's essentially combining 2 relationships.
Can anyone help me out?
User.php
public function clients()
{
return $this->hasMany(Client::class);
}
public function assessments()
{
return $this->hasManyThrough(Assessment::class, Client::class);
}
public function plans()
{
return $this->hasManyThrough(Plan::class, Client::class);
}
public function reviews()
{
// return all assessments and plans
}
public function scopeToday(Builder $query)
{
$query->whereDate('review_date', Carbon::today());
}
Client.php
public function assessments()
{
return $this->hasManyThrough(Assessment::class, Session::class);
}
public function plans()
{
return $this->hasManyThrough(Plan::class, Session::class);
}
Session.php
public function assessments()
{
return $this->hasMany(Assessment::class);
}
public function plans()
{
return $this->hasMany(Plan::class);
}

You can get a collection from both methods, so you could simply merge the 2 together. (Be warned, this will lead to ugly code later when you have to check object types during the loop.) You can't chain the scope method, since you aren't getting back a relationship object, but you could pass the date as a parameter instead, or just fix it at today's date if you'll never need other dates.
public function reviews(Carbon $date)
{
return $this
->assessments()
->whereDate('review_date', $date)
->get()
->toBase()
->merge(
$this->plans()->whereDate('review_date', $date)->get()->toBase()
)
->sortBy('review_date');
}
And then call it like this:
$date = now();
$events = Auth::user()->reviews($date);

Related

Laravel Model must return a relationship instance

My website has comments. These comments can have "votes", upvotes and downvotes.
I have a Comment Model and a CommentVote model.
In my comment model I have a functions that returns the votes:
public function votes() {
return $this->hasMany('App\CommentVote', 'comment_id');
}
public function upvotes() {
return $this->hasMany('App\CommentVote', 'comment_id')->where('vote', 1);
}
public function downvotes() {
return $this->hasMany('App\CommentVote', 'comment_id')->where('vote', -1);
}
Notice that upvotes are stored in the database in a tinyInt as 1 and downvotes are stored as -1
In my CommentVote model I have the belongsTo relationship:
public function comment() {
return $this->belongsTo('App\Comment');
}
Now I want to have a function that calculates the total "score" of the comment. Total upvotes minus total downvotes.
I try to make a function that counts all the upvotes - all the downvotes.
public function score() {
return $this->upvotes()->count() - $this->downvotes()->count();
}
This returns the error:
App\Comment::score must return a relationship instance.
In fact using the count() anywhere will return this error, despite it working fine in my other Models.
Doing something simple like:
public function voteCount() {
return $this->hasMany('App\CommentVote', 'comment_id')->count();
or even
return $this->votes()->count();
}
will return the error:
App\Comment::voteCount must return a relationship instance.
Why is this happening?
EDIT:
Here is the controller, as per requests in the comments:
public function getSubmission($subchan, $id, $URLtitle) {
$submission = Submission::where('id', $id)->first();
$comments = Comment::where('submission_id', $submission->id)->where('parent_id', NULL)->orderBy('created_at', 'desc')->get();
$comments = $comments->sortByDesc(function($comment){
return count($comment['upvotes']) - count($comment['downvotes']);
});
if (!$submission) {
return redirect()->route('home')->with('error', 'Submission not found.' );
}
return view('submissions.submission')
->with('submission', $submission)
->with('navSubchan', $submission->getSubchan->name)
->with('submissionPage', 1)
->with('comments', $comments)
;
}
I suspect you're doing $model->score, which is going to look for a function called score(), but in a specific manner that expects that function to return a HasMany, HasOne, BelongsTo etc. style relationship object.
Consider an accessor function instead.
public function getScoreAttribute() {
return $this->upvotes()->count() - $this->downvotes()->count();
}
allows you to do $model->score successfully.

Laravel: Retrieve data inputted by user

I'm quite new to Laravel and I'm confused with how I have to retrieve data inputted by certain users.
In my project, there is a user profile that should display all form submissions by the user.
Here is my controller function:
public function clientAccount(BookingRequest $bookings)
{
$client = Client::whereUserId(Auth::id())->with('user')->first();
$bookings = BookingRequest::with(Auth::id())->with('client')->first(); //unsure about here//
return view('client.account', compact('client','bookings'));
}
Here is my model:
public function client()
{
return $this->belongsTo('App\Client', 'client_id', 'user_id');
}
How do I fix this?
EDIT:
I tried using this but somehow I don't get any display
$bookings = BookingRequest::where('client_id',Auth::id());
If the relationship needs to be one to many meaning one Client has many Bookings, than in your Client model you should have the following function:
public function bookings()
{
return $this->hasMany(BookingRequest::class);
}
then you just need to find the client, and for him you just use
$client->bookings()
it will list all the bookings for that client.
Following on from nakov:
public function clientAccount()
{
$client = Client::whereUserId(Auth::id())->with('user')->first();
$bookings = $client->bookings();
return view ('client.account')->with('bookings', $bookings)
}
And in your user profile view:
foreach($bookings as $booking){
// do something with each booking
// e.g. var_dump($booking) to see the data you're working with
}
Thanks for all your responses!
I'm now able to retrieve data by using this:
public function clientAccount()
{
$client = Client::whereUserId(Auth::id())->with('user')->first();
$bookings = $client->booking()->with('client')->get();
return view('client.account', compact('client','bookings'));
}
and in my model, I used this instead
public function booking()
{
return $this->hasMany('App\BookingRequest', 'client_id', 'user_id');
}

Laravel collection returning duplicate

Hi I am trying to return a Users Schedule where greater than or equal to todays date, or if that schedule contains a relation (ScheduledYard) that is not complete:
$schedules = Auth::user()
->schedules()
->where('due_at', '>=' , Carbon::now())
->orWhereHas('scheduledYards', function ($query) {
$query->where('completed', false);
})->get();
$schedules = $schedules->sortBy('due_at');
return ScheduleResource::collection($schedules);
This works perfectly fine, but for some reason it is duplicating this for each user it is attached to. i.e if 3 users have this schedule then it is returned 3 times for the Authenticated user.
User Model:
public function schedules()
{
return $this->belongsToMany('App\Models\Schedule');
}
Schedule Model:
public function users() {
return $this->belongsToMany('App\Models\User');
}
If I remove the orWhereHas() it will return a single object. What am I doing wrong here and how can I return a single object based on the requirements above?
In Schedule Model, you need to change the relationship type. i assuming its one to many relationship between users and schedules. in that case change users() to user() and method body as below.
public function user() {
return $this->belongsTo('App\Models\User');
}
In User model the correct relationship should be defined as:
public function schedules()
{
return $this->belongsTo('App\Models\Schedule');
}
Please try with distinct. For example,
public function schedules()
{
return $this->belongsToMany('App\Models\Schedule')->**distinct**();
}

Laravel 4 Eloquent relations query

I have a project with main table 'Qsos' and bunch of relations. Now when I try to create advanced search I don't really know how to query all relations at the same time. Qso model has following:
public function band()
{
return $this->belongsTo('Band');
}
public function mode()
{
return $this->belongsTo('Mode');
}
public function prefixes()
{
return $this->belongsToMany('Prefix');
}
public function user()
{
return $this->belongsTo('User');
}
public function customization() {
return $this->hasOne('Customization');
}
Then I have SearchController with following code that has to return collection of all Qsos following required conditions:
$qsos = Qso::withUser($currentUser->id)
->join('prefix_qso','qsos.id','=','prefix_qso.qso_id')
->join('prefixes','prefixes.id','=','prefix_qso.prefix_id')
->where('prefixes.territory','like',$qTerritory)
->withBand($qBand)
->withMode($qMode)
->where('call','like','%'.$input['qCall'].'%')
->orderBy('qsos.id','DESC')
->paginate('20');
And then in view I need to call $qso->prefixes->first() and $qso->prefixes->last() (Qso and Prefix has manyToMany relation) but both return null. What is wrong?
Here is the eloquent code that I found working but taking VERY long time to process:
$qsos = Qso::withUser($currentUser->id)
->with('prefixes')
->withBand($qBand)
->withMode($qMode)
->where('call','like','%'.$input['qCall'].'%')
->whereHas('prefixes', function($q) use ($qTerritory) {
$q->where('territory','like',$qTerritory);
})
->orderBy('qsos.id','DESC')
->paginate('20');

Laravel 5.1 Querying Relationship

Learning Laravel by building a stock portfolio app. Have models for Users, Accounts, Stocks, Options, and Transactions. I believe I have the relationships set up properly.
Question is how do I get a Users->Account->Transactions. I'm sure I could just do something in query builder but I was hoping for a more "eloquent" approach.
User
public function accounts()
{
return $this->hasMany('App\Account');
}
public function stocks()
{
return $this->belongsToMany('App\Stock');
}
public function options()
{
return $this->belongsToMany('App\Option');
}
public function transactions()
{
return $this->hasMany('App\Transaction');
}
Account
class Account extends Model
{
protected $fillable =
[
'name',
'broker'
];
public function user()
{
return $this->belongsTo('App\User');
}
public function stocks()
{
return $this->belongsToMany('App\Stock');
}
public function options()
{
return $this->belongsToMany('App\Option');
}
public function transactions()
{
return $this->hasMany('App\Transaction');
}
Transaction
class Transaction extends Model
{
protected $fillable =
[
'type',
'account_id',
'transaction_date',
'quantity',
'stock_id',
'option_id',
'amount',
'description'
];
public function user()
{
return $this->belongsTo('App\User');
}
public function stock()
{
return $this->belongsTo('App\Stock');
}
public function option()
{
return $this->belongsTo('App\Option');
}
public function account()
{
return $this->belongsTo('App\Account');
}
public function accounts()
{
return $this->hasManyThrough('App\Account', 'App\User');
}
}
Ultimately, I guess I would be looking for a total amount for each account a user may have. (User can have many accounts and each account would have many transactions, stocks and options.)
Thanks for any help or at least letting me know if I am going down the right road!!
If $user is a User object then $user->accounts is a collection of Eloquent models
You can load all of the accounts and their transactions with eager loading:
$user->load([
'accounts',
'accounts.transactions',
]);
But $user->accounts->transactions isn't something you can do because "transactions" is a relation on each individual Account object, not on the collection of account objects. So to get the transactions you'd loop through each account:
foreach ($user->accounts as $account) {
// do something with $account->transactions;
}
I highly don't recommend doing this, as #alexw mentioned in his comment, Eloquent objects are pretty large and having x many x many is an exponentially large amount of data you're loading into memory. But generally, that's how you'd access relations of relations.
You're better off using the query builder
The good news is that relations can make querying really easy! For example, you could do something like this instead:
Note: in Laravel 5.2, the lists method has been replaced with pluck
$user->load(['accounts']);
foreach ($user->accounts as $account) {
$amounts = $account->transactions()->lists('amount');
$total = $amounts->sum();
// - or -
$query = $account->transactions()
->select(\DB::raw('SUM(amount) AS total'))
->first();
$total = $query->total;
}

Resources