Laravel working with relations that arent related - laravel

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();
}]);

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.

Laravel how to get only relation data

this is my User.php relation code
public function activities()
{
return $this->hasMany(Activities::class, 'builder');
}
and this is my query to get only relation data
return User::whereHas('activities', fn($query) => $query->where('user_id', 1))
->paginate();
but it returns only user data without appling any relation, and its pagination not get to use pluck
i have also tried this
User::where('username', request()->username)->has('activities')->paginate();
but i need only get relation data not user with relation, and i prefer do it with whereHas
You need to create reverse relation for Activities model:
class Activities extends Model
{
// ...
public function user(): BelongsTo
{
return $this->belongsTo(User::class, 'user_id');
}
}
And get all the activities using Activities builder:
$paginated = Acitivities::query()
->whereHas('user', static function(Builder $userQuery) {
$userQuery->where('name', request()->username); // Think about security!
})
->paginate();
Note: All the models must be named in singular (e.g. Activity, User, Product and etc.).

How to return collection in reverse order

In my Conversation model I have
protected $with = ['messages', 'users'];
How would I change the order that it returns the 'messages' to DESC?
Usual Eloquent relationship looks something like this:
class Category extends Model
{
public function products()
{
return $this->hasMany('App\Product');
}
}
So here’s what we should do to automatically order products by title in every query that uses this relationship:
public function products()
{
return $this->hasMany('App\Product')->orderBy('name');
}
That’s it, everything is “in order” now!
Source: https://laraveldaily.com/eloquent-relationships-with-automatic-orderby/
Try this on your controller, it will order messages in descending order
User::with(['messages' => function($query) {
$query->orderBy('id', 'DESC');
}])->get();
for more see documentation

Trying to display eloquent data base from ID's

Hi I am trying to create a record base from ID of an order transaction.
I have here the ORDERS, CAMPANIES and CURRENCIES models.
Now I want the ORDER to get the ID from a URL Parameter, and then the companies should grab the company_id from Orders same with currency, I want to grab the currency_id from orders.
Here's what I came up so far:
public function create($order_id)
{
$order = Orders::find($order_id)->where('id', '=', $order_id)->get();
$company = Companies::where('id', '=', $order['company_id'])->get();
$currency = Currencies::where('id', '=', $order['company_id'])->get();
$banks = Banks::all('name','acronym','id')->get();
return view('orderPayments.create', compact('order'))
->with('company', $company)
->with('currency', $currency)
->with('banks', $banks);
}
currencies model
public function orderPayments()
{
return $this->hasMany('App\Orderpayments', 'id','currency_id');
}
Companies Model
public function orderPayments()
{
return $this->hasMany('App\Orderpayments', 'id','company_id');
}
Order Payments Model
public function company()
{
return $this->hasOne('App\Companies', 'id','company_id');
}
public function currency()
{
return $this->hasOne('App\Currencies', 'id', 'currency_id');
}
public function bank()
{
return $this->hasOne('App\Bank', 'id', 'currency_id');
}
How can I achieve it? thank you so much in advance!
UPDATE
I just applied #sandy's answer. I checked if the $order has a content so I echod the $order by doing this, {{ $order }} The return value is ok.
but when I calling the other attributes like $order->grandtotal, or $order->companies->comp_name the error is
If your relationship between your models is a One To Many relationship you should use belongsTo in your relations like this:
OrderPayment Model
public function company()
{
return $this->belongsTo('App\Companies','company_id');
}
public function currency()
{
return $this->belongsTo('App\Currencies','currency_id');
}
public function bank()
{
return $this->belongsTo('App\Bank', 'currency_id');
}
Also, the second parameter on the function should be your foreign key.
Answering your question:
If you want to access the relations of a given Order you could do this:
$order = Order::find($order_id);
$order->company; // this will return the company
$order->currency; // this will return the currency
If you want to display in your view:
{{$order->company->anyCompanyAttribute}}
Note:
You should name your models in SINGULAR like Currency, Company. Not in plural.
try like this
$order = Orders::with(['company','currency'])->where('id', '=', $order_id)->get();

Laravel Sum of relation

I want to return the sum of "amount" from my payments table. There can be many payments for one invoice. The below "->sum('amount') does not work, it returns:
Call to a member function addEagerConstraints() on a non-object.
How to return the sum of all payments for each invoice in my relation?
Invoices Model:
class Invoices extends Eloquent {
public function payments()
{
return $this->hasMany('Payments')->sum('amount');
}
}
Expenses Model:
class Payments extends Eloquent {
public function invoices()
{
return $this->belongsTo('Invoices');
}
}
My table "payments" holds the foreign key of my tables invoices, which is invoices_id.
Starting by Laravel 8 you can simply use withSum() function.
use App\Models\Post;
$posts = Post::withSum('comments', 'votes')->get();
foreach ($posts as $post) {
echo $post->comments_sum_votes;
}
https://laravel.com/docs/8.x/eloquent-relationships#other-aggregate-functions
class Invoices extends Eloquent {
public function payments()
{
return $this->hasMany('Payments');
}
}
class Payments extends Eloquent {
public function invoices()
{
return $this->belongsTo('Invoices');
}
}
In your controller
Invoice::with(['payments' => function($query){
$query->sum('amount');
}])->get();
;
You can show this package
$invoices = Invoices::withSum('payments:amount')->get();
First decide which Invoice (for example id 1)
$invoice = Invoices::find(1);
Then eager load all the corresponding payments
$eagerload = $invoice->payments;
Finally assuming you have the amount field in your Invoice model you can simply find the sum using the method below:
$totalsum = $eagerload->sum('amount');
This is also possible. we can do by model itself.
class Invoices extends Eloquent {
public function payments()
{
return $this->hasMany('Payments')
->selectRaw('SUM(payments.amount) as payment_amount')
->groupBy('id'); // as per our requirements.
}
}
}
Note
SUM(payments.amount)
payments is tableName
amount is fieldName
I found a simple way to acomplish this in here, you can use withPivot() method.
You can redefine a bit your relation to something like following
public function expenses()
{
return $this->belongsToMany('Expenses', 'invoices_expenses')
->withPivot('name', 'amount', 'date');
}

Resources