Laravel paginate belongsToMany relationship? - php

I would like to paginate my results I get from my relationship. I have a products table/controller, a users table/controller and a markedProducts table. However, I don't have a markedProducts table because I don't actually need it. I use this solution to get all marked products of one user in my user model:
public function markedProducts(){
return $this->belongsToMany('App\Product', 'products_marks');
}
My question now is how can I paginate my results and my second question is how can I get the created_at value of each row in my markedProducts table?
Because my solution is only returning the marked products. However, I would like to show the user when he has marked the product.
So far I had this idea:
$markedProducts = DB::table('products_marks')->where('user_id', Auth::id())->orderBy('created_at', 'desc')->paginate(2);
$products = Product::whereIn('id', $markedProducts->lists('product_id'));
However, ->lists() is not a method anymore and I actually don't want to bypass the Laravel methods and I don't want to loop through my pagination to get an array with all product_id's.
Do you guys have any good and performant solution?
Kind regards and thank you!

Auth::user() will return the authenticated user.
And use relationship method markedProducts() to get the query of products.
And then orderBy the column created_at of pivot table.
At last, apply paginate to this eloquent builder.
Auth::user()->markedProducts()->orderBy('products_marks.created_at', 'DESC')->paginate($limit);

Related

How to get products based on a count on a field in this table with Laravel

I'm working on Laravel 5.8
Let's say we have a table products like this:
id | product_type_id |...
1 |______1_______|...
2 |______2_______|...
3 |______2_______|...
4 |______3_______|...
I would like to know how to get the all the products which "share" a product type.
In other words, I would like to get all the products except those whose product_type_id is unique in the table.
I know who to do it in a foreach loop but I would like to take advantage of the resources of using Laravel.
Thanks in advance.
The Laravel way of doing it would be using Eloquent relationships along with has() and whereHas(), like this:
$products = Product::whereHas('type', function ($builder) {
$builder->whereKey(Type::has('products', '>=', 2)->pluck('id'));
})->get();
I'm assuming you have defined Product and Type models and connected them together:
Product belongsTo Type
Type hasMany Product
If I'm understanding your question correctly you could do something like this.
First, make sure your ProductType model has a products relationship defined.
(I assume it does based on your Products table.)
Then you can query based on a relationship count using the Eloquent model method has.
Example:
ProductType::with('products')->has('products', '>', 1)->get();
The with('products') is optional. It simply grabs the Products at the same time to avoid additional queries. The whereHas method also works but is really only necessary if you need to filter the relationship based on more complex parameters.
You could also use the has/whereHas method inside the Product model using an inverse (ie belongsTo) relationship to get the same thing but inverted. It really depends on how you want the data presented to you.
Example:
Product::whereIn(
'product_type_id',
ProductType::has('products', '>', 1)->pluck('id')
)->get();
To sum it all up the first way will give you:
ProductType => Product
While the second example will give you:
Product => ProductType
See the related Laravel documentation for more info.
Hope that helps!

Laravel 5.2 Display each users posts they've made this week as count

I'm trying to display a list of all the users and how many posts they've each made this week, and how many they've made last week, inside of a form
I've used a hasMany relation between the two tables and the relationship it's self is working.
return $this->hasMany('App\applications', 'user_id');
inside of the view what I have that's displaying the post count is
{{$user->applications->count()}}
The main thing I'm stuck on is the SQL Query or using Carbon inside of the controller function to achieve this.
If anyone has done this before your help would be greatly appreciated!
Thank you.
Since you want to count posts for many users, eager load the data and use withCount():
User::withCount(['applications' => function($q) {
$q->whereBetween('created_at', [Carbon::now()->startOfWeek(), Carbon::now()]);
}])->get();
If you want to count the number of results from a relationship without actually loading them you may use the withCount method, which will place a {relation}_count column on your resulting models.
https://laravel.com/docs/5.4/eloquent-relationships#counting-related-models

Laravel model object retrieving relation model with where condition without "get" method

I'm working on octoberCMS(laravel framework), I have a doubt on retrieving relation model on where clause.
I have fetched a clothing_type record from "clothing type" model based on its primary key "id".
$clothing_type = ClothingType::where('id',post('clothingtype_id'))->where('status','Active')->first();
This "clothing type" model is related with "products" model, the relation is => each clothing type hasMany products.
Every thing works fine; Now my business logic has two cases, one is to get all the products of the clothing type and another is to get the first product of the clothing type. So I have used the $clothing_type->products to get all the products and $clothing_type->products->first() to get the first product.
Now I have to apply a condition for both the cases. The condition is that only the product whose status is "Active" should be fetched, hence
$products = $clothing_type->products->where('status','Active'); and$first_product_detail = $products->first();.
Every thing works as expected but how come the products are fetched without "get()" method. $clothing_type->products->where('status','Active')->get(); Since I'm new to relation I want to know how this works or is this a bad way to get records or improper assumption. But every thing works good.
$clothing_type = ClothingType::where('id',post('clothingtype_id'))->where('status','Active')->first();
if(count($clothing_type->products))
{
$products = $clothing_type->products->where('status','Active');
$first_product_detail = $products->first();
}
You are doing it the correct way. When you access the relationship as an attribute Eloquent automatically retrieves the records.
However, if you access the relationship as a method, you get the query itself, to which you can add your filters:
if(count($clothing_type->products))
{
$products = $clothing_type->products()->where('status','Active')->get();
$first_product_detail = $products->first();
}
This would solve your problems
(documentation is over here (see the first item))
Edit: Also note that the first method is not a method of Eloquent, but from Collection, which is pretty powerful!
Edit2:
I misread the part of your question where you want to know HOW this is possible. Both Eloquent and Collections have a where method. I assume you understand the working of the Eloquent one, but the one from Collection is pretty much the same (see documentation on the Collection where here)
I prefer the Eloquent one myself, because that limits the amount of records that is retrieved from the database. But if you need all the products (even the inactive ones) later on in your code, just use the Collection method to filter the active ones out
There is nothing to be afraid of...
first() and where()
are functions of both Illuminate\Database\Query\Builder as well as Illuminate\Support\Collection and all first does is limit the records to take 1 and then give you the first record. When you use Builder a query is made to get 1 record and 1 you use it on a collection, all records are first get() and then the first of those records is returned.
Here,
When you do,
$clothing_type->products, Laravel gives you a collection of products...
So...
$products is an object of Illuminate\Support\Collection
and
$products->first() calls for the first() function in that class.
Documentation on where and first methods of a collection...

How to return a record with its relation only if the relation has not been deelted

I am using Laravel 5.2 and have some relationships between my models setup.
I have a Product model and a Customer model (a product has a customer).
I am using the following to get a list of customers, however I am also using soft deletes. If the customer has been soft deleted (the relation), I don't want to return the product.
How do I achieve this in Laravel?
$products = Product::with('customer')->get(); --want to say "where customer.deleted_at is null"
You have to call has on the query:
$products = Product::with('customer')->has('customer')->get();
As #Lock put it, use
$products = Product::with('customer')->has('customer')->get();
TIP: For the opposite - i.e to get only the products with deleted customers (doesn't make much sense here but you may find this handy in future), use below
$deleted=Product::with(['customer' => function ($q) {
$q->withTrashed();
}])->onlyTrashed()->get();

Laravel eager loading issue

Say I have an orders table and a users table and a guests table, for a company who accept orders from both users and guests.
I also have a is_guest boolean on the orders table which lets me know quickly whether the order has been made by a guest or a registered user.
I have a model method called customer which returns the eloquent relation like so:
public function customer()
{
return $this->is_guest ? $this->belongsTo('Guest', 'user_id') : $this->belongsTo('User', 'user_id');
}
This works very well when I pass orders to the view and access the customer's data like so:
// Maps to either $order->user->firstname OR $order->guest->firstname
echo $order->customer->firstname;
But when I try to use this functionality when using eager loading it doesn't quite work:
$orders = Order::with('customer')->get();
return Response::make($orders);
It only returns the eager loaded customer data for either the user or the guest and never both.
I'm wondering why this is and how to get round it?
Is it because of the way the eager loading works - it only calls the customer() method once just as it only hits the database once, so whatever the first result of the query is, it will use that to determine the rest of the query too?
If I am trying to get a JSON response of all the order data with all of the customer's details too, how would I do that?
Thanks.
UPDATE
Yes, turns out I was abusing the Laravel relationship method by adding a condition.
Using the polymorphic relations is a much better approach as suggested by #lukasgeiter.
http://laravel.com/docs/4.2/eloquent#polymorphic-relations
I did have trouble loading other relations based on the polymorphic relation. For example if both User and Guest had corresponding tables UserDetail and GuestDetail then I couldn't get the eager loading to work. So I added this to both the User and Guest models:
protected $with = ['detail'];
This means that whenever the customer polymorphic relation is eager loaded, then both User and Guest will load their relationships automatically. I think I could have also used the 'lazy loading' functionality $order->load('customer') but I didn't try it out.
public function detail()
{
return $this->hasOne('UserDetail');
}
Thanks guys.

Categories