How can I link 2 existing models in my laravel code? - laravel

I have two models customer and orders. They are already fecthed separately
$customers = customer::all();
$orders = orders::all();
customerID=1 has orderID : 1, 2,4 customerID=2 has orderID : 3,5,9
They are related (hasMany, belongsTo) but the problem is inside my for a certain reason they are separated but I want to send them as response in API using toJson or ToArray as one data having the orders nested to their correct customers.
How can I achieve that linking to have at the end one variable $customersWithOrders that should be transformed to JSON ?
I am using laravel 5.5

I don't know what the context is. Defining relationships as other answers mentioned is a good solution.
In addition, I recently read a pretty good article about this specific scenario.
So you can also do something like this, if you have already retrieved customers and orders:
$customers = Customer::all();
$orders = Order::all();
return $customers->each(function ($customers) use ($orders) {
$customer->setRelation('orders', $orders->where('customer_id', $customer->id));
});

If you already have a relation you just use it. For example, in model Customer.php:
public function orders()
{
return $this->hasMany(Order::class);
}
Then you'd get customer orders by calling $customer->orders

If you already have defined relations, you can simply fetch data with eager loading
// in customer model
public function orders()
{
return $this->hasMany(orders::class, 'orderID');
}
// in controller
$customersWithOrders = customer::with('orders')->get();
return response()->json(['customersWithOrders' => $customersWithOrders]);
// in js
for (let customer in response.customersWithOrders){
let orders = customer.orders
}

Related

Is laravel seeding as hard as I see it or I'm stumbling with something that's not that complicated?

The last few days I've been rocking my head against the wall with the seeders. I can't seem to get the hang of it.
The relationships are very simple:
A Brand hasMany products and each product belongs to a single brand.
A Category hasMany products and each product belongs to a single category.
Given that, I'm creating 5 categories at the beginning so I can retrieve a random one later.
I'm also creating 10 brands, and for each brand I'm creating 50 products and make them belong to that brand. Then I create the relationship with the product and the category, retrieving a random category for each product.
Im getting this error:
PDOException::("SQLSTATE[HY000]: General error: 1364 Field 'brand_id' doesn't have a default value")
I dont understand why I'm getting this error because I'm creating the relationship prior to creating the products:
$brand->products()->saveMany(factory(App\Product::class, 50).
public function run()
{
$users = factory(\App\User::class, 1000)->create();
$categories = factory(\App\Category::class, 5)->create();
factory(App\Brand::class, 10)->create()->each(function ($brand) {
$brand->products()->saveMany(factory(App\Product::class, 50)->make()->each(function ($product) use ($brand) {
$product->category()->associate($this->getRandomCategory());
$product->save();
}));
});
}
private function getRandomCategory() {
return \App\Category::all()->random();
}
private function getRandomUser() {
return \App\User::all()->random();
}
I don't know if I'm making a big deal out of seeding but it just seems complex to me. Maybe I'm taking a wrong approach using the factories. Is there any great tutorial for seeding out there?
Thanks in advance!
This is actually not a seeding problem. The PDO-driver already tells you about the issue:
brand_id does not a have a default value
Probably Laravel assumes, that the id column does not need a default and therefore does not insert it with an id. But the column seems to have no default definition in the database (should be smth. like AUTO_INCREMENT). And that's the reason why you receive an error from the database.
The problem was this one:
The saveMany method gets called after the each function is done, so I was kinda saving the product before it added the brand relationship. Thats why it couldnt assign the foreign_key brand_id. This is the block of code working:
public function run()
{
$users = factory(\App\User::class, 1000)->create();
$categories = factory(\App\Category::class, 5)->create();
factory(App\Brand::class, 10)->create()->each(function ($brand) {
$brand->products()->saveMany(factory(App\Product::class, 50)->make()->each(function ($product) use ($brand) {
$product->category()->associate($this->getRandomCategory());
}));
});
}
private function getRandomCategory() {
return \App\Category::all()->random();
}
private function getRandomUser() {
return \App\User::all()->random();
}

How to do the following with doesn't have or other optimized way in laravel

I have a thread which gave the answer but later on I found that I was getting limitations: how to get list of users who are not under belongsToMany relation under a table in laravel?
so creating a new thread where I do have an answer but now how can I optimize the same with any prebuild functions like doesntHave or something entirely else.
below is the code which gives me the list of users who are under a group and not assigned any task. one group can have multiple tasks so only users where the task is not assigned needs to be listed.
$gid = $task->group_id;
$MembersList = $task->members;
$group_subscribers = Group::with(['subscribedUsers' => function($q){
$q->select('id');
}])->whereId($gid)->get();
$group_subscribers = $group_subscribers[0]->subscribedUsers->pluck('id')->toArray();
$alreadyMembers = DB::table('task_user')->select('user_id as id')->whereIn('user_id', $group_subscribers)->pluck('id')->toArray();
$finalList = array_diff($group_subscribers, $alreadyMembers);
$users = User::whereIn('id', $finalList)->get();
return $users;
any way to improve the above code?
I guessed Users and Tasks was in a many to many relationship. For my optimisation to work, i added the following relationship to the User model, this is gonna be used when we filter the subscribed users.
public class Users {
public function tasks() {
return $this->belongsToMany(Task::class);
}
}
Basically the implementation is filtering all the subscriber users and checking if they have tasks, since tasks is a relationship it returns a Collection and isNotEmpty() is used for that reason.
$groupId = $task->group_id;
$group = Group::with(['subscribedUsers.tasks'])->find($groupId));
$withoutTasks = $group->subscribedUsers->filter(function ($user) {
return $user->tasks->isNotEmpty();
} );
return $withoutTasks;
It is a rule of thumb to avoid DB:: methods and you should be able to go to model to model, instead of making queries from getting from one model to another.

Laravel / Eloquent: Is it possible to select all child model data without setting a parent?

I have various parent/child relationships, drilling down a few levels. What I want to know is if its possible to do something like this:
$student = Student::find(1);
$student->bursaries()->enrolments()->courses()->where('course','LIKE','%B%');
(With the end goal of selecting the course which is like '%B%'), or if I would have to instead use the DB Query builder with joins?
Models / Relationships
Student:
public function bursaries() {
return $this->hasMany('App\StudentBursary');
}
StudentBursary:
public function enrolments() {
return $this->hasMany('App\StudentBursaryEnrolment');
}
If what you want is to query all courses, from all enrollments, from all bursaries, from a students, then, unfortunately, you are one table too many from getting by with the Has Many Through relationship, because it supports only 3 tables.
Online, you'll find packages that you can import / or answers that you can follow to provide you more though of solutions, for example:
1) How to use Laravel's hasManyThrough across 4 tables
2) https://github.com/staudenmeir/eloquent-has-many-deep
Anyhow, bellow's something you can do to achieve that with Laravel alone:
// Eager loads bursaries, enrolments and courses, but, condition only courses.
$student = Student::with(['bursaries.enrolments.courses' => function($query) {
$query->where('course','LIKE','%B%');
}])->find(1);
$enrolments = collect();
foreach($student->bursaries as $bursary) {
$enrolments = $enrolments->merge($bursary->enrolments);
}
$courses = collect();
foreach ($enrolments as $enrolment) {
$courses = $courses->merge($enrolment->courses);
}
When you do $student->bursaries() instead of $student->bursaries, it returns a query builder instead of relationship map. So to go to enrolments() from bursaries() you need to do a bursaries()->get(). It should look like this.
$student->bursaries()->get()[0]->enrolments(), added the [0] because im using get(), you can use first() to avoid the [0]
$student->bursaries()->first()->enrolments()
But I'm not sure if it will suffice your requirement or not.

Deleting nested comments and related data from laravel controller

ive implemented nested comments in laravel with parent_id and there's another table votes where related data's are stored.
I've hasMany relation defined in comments model. Now when i delete a comment, it should delete all its replies and votes as well.
To delete votes i used
$review->votes()->delete();
which works perfectly. but i'm stuck with deleting votes for nested replies.
If i use foreach loop how to loop inside all levels which is dynamic.
public function deletereview($id=null){
$review = Review::find($id);
foreach($review->replies as $reply){
$reply->votes()->delete();
//how to do this for all levels?
$reply = $reply->votes(); // this doesn't work
}
return back();
}
Kindly advise on the proper way of doing it.
Note : i've read through the cascade options from migrations but that doesn't explain anything for nested comments(reply of replies and its related data's).
Thanks
-Vijay
// Review Model
public function deleteRelatedData() {
// Delete all votes of this review
$this->votes()->delete();
// Calling the same method to all of the child of this review
$this->replies->each->deleteRelatedData();
}
// Controller
public function deletereview($id=null){
$review = Review::find($id);
$review->deleteRelatedData();
return back();
}
I would recommend use observer for this.
https://laravel.com/docs/5.8/eloquent#observers
public function deleted(Review $review)
{
foreach($review->replies as $reply){
$votes = $reply->votes;
Votes::destroy($votes)
}
Destroy method allow you to delete multiple models.
For any next level you have to use another foreach loop in this case.
$reply = $reply->votes(); doesn't work since you should use
$votes = $reply->votes;
//or
$votes = $reply->votes()->get();

Getting sum of field through polymorphic relationship in Laravel

I have two models, Payments_Distribution and Donation.
In my Donation model:
public function payments() {
return $this->morphToMany(Payments_Distribution::class, 'payable');
}
And I can save a payment distribution to the donation model using the following:
$distribution = new Payments_Distribution;
$distribution->payment_id = $payment->id;
$amount = $request->payment_details['amount'][$i];
$donation->payments()->save($distribution);
But I'm stuck on how to retrieve the sum of all the associated records' amount fields in the Payment_Distribution model table.
Would it be something like:
$donation->payments()->______ ->sum('amount');
Or something else? I'm still a bit new to polymorphic relationships.
In your code, you are not saving amount to the Payments_Distribution instance before using the ->save() method. Replace:
$amount = $request->payment_details['amount'][$i];
With:
$donation->amount = $request->payment_details['amount'][$i];

Resources