Laravel Eloquent "with" return relations with limit - laravel

I have simple Laravel relations between Category and Article models. I need to return the list of all categories with related articles.
Test data: 3 categories and 15 articles (5 articles per category).
Expected result: collection of all categories with 2 latest related articles for each category
By default case I should use something like:
Category::with('articles')->get();
It's ok - I got 3 categories. Each category has "relations" with 5 related records.
Next, I tried:
Category::with(['articles' => function ($query) {
$query->orderBy('created_at', 'desc');
$query->limit(2);
}])->get();
But this is the wrong way. This construction in "raw" query looks like:
select * from `articles` where `articles`.`category_id` in (select id from categories) limit 2
So it's returned me a wrong result as expected...
I need all categories and latest 2 articles for each category (3 * 2 = 6 records) this query returned just 2 records

There is no native support for this in Laravel.
I created a package for it: https://github.com/staudenmeir/eloquent-eager-limit
Use the HasEagerLimit trait in both the parent and the related model.
class Category extends Model {
use \Staudenmeir\EloquentEagerLimit\HasEagerLimit;
}
class Article extends Model {
use \Staudenmeir\EloquentEagerLimit\HasEagerLimit;
}
Then you can apply ->limit(2) to your relationship.

Related

Laravel Eloquent Query, Many to Many relational data take() not working

I have 'Product' and 'Brand' model Many to Many Relation
I need to fetch data from Brand with limited (5 products ) products so My query is
$brands = Brand::orderBy('order_by', 'asc')->where('status', 1)->with('product')->take(6)->whereHas('product', function ($product){
$product->take(5);
})->get();
here $product->take(5) not works for me I get all data which are associated with that Brand
you can use the trait: HasEagerLimit:
you can install it:
composer require staudenmeir/eloquent-eager-limit:"^1.0"
and then in your model:
class Brand extends Model {
use \Staudenmeir\EloquentEagerLimit\HasEagerLimit;
and also in Product model:
class Category extends Model {
use \Staudenmeir\EloquentEagerLimit\HasEagerLimit;
}
now : when you load your relation ... it should return the result you desire:
$brands = Brand::orderBy('order_by', 'asc')->where('status', 1)->with(['product'=>function ($product){
$product->take(5);
}])->take(6)->get();
more details in: https://github.com/staudenmeir/eloquent-eager-limit

Laravel, many-to-many relationship among multiple models

I Have multiple models that have many to many relationship
Here are the models
News Section
Categories
Sub Categories
Approved News
Pending News
Each News Section can have multiple Categories.
Each Category can have multiple Sub Categories.
Each Sub Category can has multiple Approved News and Pending News.
I want to have News with Categories, Sub Categories and Pending / Approve news
and stuff like
Categories with Sub Categories and Approve news
I tried with pivot tables but not able to get results
Models are as follow
News Section
class NewsSection extends Model
{
public function categories()
{
return $this->belongsToMany(Category::class);
}
}
Category
class Category extends Model
{
public function subcats(){
return $this->belongsToMany(SubCategory::class);
}
public function newssections(){
return $this->belongsToMany(NewsSection::class);
}
}
SubCategory
class SubCategory extends Model
{
public function category(){
return $this->belongsTo(Category::class);
}
public function approvednews(){
return $this->belongsToMany(ApprovedNews::class);
}
public function pendingnews(){
return $this->belongsToMany(PendingNews::class);
}
}
ApprovedNews
class ApprovedNews extends Model
{
public function subcategories (){
return $this->belongsToMany(SubCategory::class);
}
}
PendingdNews
class PendingdNewsextends Model
{
public function subcategories (){
return $this->belongsToMany(SubCategory::class);
}
}
Update
This what I have done so far
$news =Category::with('subcats.approvednews')->where('id',1)->get();
I got all the approved news with subcategories and categories
how can i modify this to get specific subcats and approved news per category, if i do this
$news =Category::with('subcats.approvednews')->where('subcats.id',1)->get();
I get an error like id ambiguous
Is it possible to pick and chose items from relation for instance return just 2 subcats and 3 approved news for each subcat of selected category
or
get count of approved news and pending news per subcat and category
Thanks in advance
The error "error like id ambiguous" means that you need to specify the table in your where('id', 1) like where('table.id', 1) so that MySQL knows which id column in which table you mean.
You can constrain the models returned by with like this:
Category::with(['subcats' => function(Builder $query) {
$query->where('id', '=', 1);
}]);
Also you can count relations:
$subcat = SubCategory::withCount(['approvednews']);
$subcat->approvednews_count;
Limiting eager loaded relations is not possible per the docs.
A workaround may be to go the other way round starting from ApprovedNews:
ApprovedNews::whereHas(['subcategories' => function(Builder $query) {
$query->where('id', '=', 1);
}])->limit(10);
I have a few suggestions of how you can get this to work. In your comments, you say you are getting an issue doing the following:
$items=Category::with('subcategory')->where('id',1)->get();
Where is 'subcategory' coming from? By the looks of your model, your relationship between Category and Subcategory is called subcats. So you would need to change it to:
$items=Category::with('subcats')->where('id',1)->get();
And if you dump that out, you should see that you will get the category where the ID is 1, and the subcategories loaded in. A way to test that your relationships are working would be something like this:
$category = Category::find(1);
$subCats = $category->subcats()->get();
dd($subCats);
In your relationships, instead of using SubCategory::class I would suggest trying return $this->belongsToMany('App\SubCategory'); so that the models are definitely connected.
Once you have tested that your relationships between one another work, you can get started on testing that you can go from a->b->c etc.
May be using "Nested Eager Loading" and "scope", you can do something like
$pendings = NewSection::with('categories.subCategories')->pending()->get()
$approved = NewSection::with('categories.subCategories')->approved()->get()
not tested it, but you can try, may be with some modification, you can reach to your goal.
if you want return one collection, you may like to merge it
$approved->merge($pendings);
but, you should avoid it.

Scope query in a many to many relationship

I created 2 models, "Post" and "Category". This is a many to many relationship, works great.
My tables are the following :
alex_blog_posts : where posts are stored with columns like "title", "published" etc...
alex_blog_categories : where categories are stored with columns like "title", "parent_id" etc...
alex_blog_posts_categories : where the relation is stored between posts and categories with columns "post_id", "category_id"
Let's assume I want to filter all posts that are associated to a category with name : "Category 1"
public function scopeFilterCategory($query) {
$query->join(????); // My problem is to replace the ???
$query->where('title', '=', 'Category 1');
return $query;
}
I'm not familiar enought with october and laravel yet and I'm stuck here. Probably very simple for laravel expert but I need a concrete example of something working cause all things I tried failed :/
Thanks for your help
laravel have the "whereHas":
https://laravel.com/docs/5.5/eloquent-relationships#querying-relationship-existence
On the post model you need the write this query:
$posts = Post::whereHas($relationName, function ($query) {
$query->where('title', =, 'Category 1');
})->get();
$relationName - should be the name of the function that define the relation in your model (etc: 'categories')
For Laravel
There is a good answer on Laracasts
Scope for many to many relation
The following is an improved version.
Assume we have authors & articles many-to-many relation (with a pivot authors_articles);
Assume we have belongsToMany relation defined in both models;
// in Article model
public function scopeFromAuthor($query, $authorId)
{
return $query->whereHas('authors', function ($query) use ($authorId) {
$query->where('authors_articles.author_id', '$authorId');
});
}

Complicated Eloquent relationship using `Model::query`

I have a complicated relationship I'm trying to establish between two models.
The goal is to use $supplier->supply_orders to access the orders where the user supplies an item.
This throws: LogicException: Relationship method must return an object of type Illuminate\Database\Eloquent\Relations\Relation.
With the code I've got I can use $supplier->supply_orders()->get(), however, when I try to use it as a relationship it throws. Since this is a relationship I should be able to wrap it in a relationship, but how would I go about doing that?
Supplier Model:
class Supplier extends Model {
public function supply_orders() {
return Order::query()
->select('order.*')
->join('item_order', 'order.id', '=', 'item_order.order_id')
->join('item', 'item_order.item_id', '=', 'item.id')
->where('item.supplier_id', '=', $this->id);
}
}
~~~ A whole lot of back info that I don't think you need but might ~~~
sql tables:
supplier
- id
items:
- id
- supplier_id
item_order:
- id
- order_id
- item_id
orders:
- id
The other Eloquent Models:
class Item extends Model {
public function orders() {
return $this->belongsToMany('Order');
}
}
class Order extends Model {}
Example of how this should work:
$supplier = factory(Supplier::class)->create();
$item = factory(Item::class)->create([
'supplier_id' => $supplier->id,
]);
$order = factory(Order::class)->create();
$order->items()->attach($item);
$orders = $supplier->supply_orders // Throws LogicException
This throws: LogicException: Relationship method must return an object of type Illuminate\Database\Eloquent\Relations\Relation
Sounds like a hasManyThrough with a many to many relationship. Laravel has no inbuilt support for this but you can always go ahead and write your own relationship like this: https://laravel.io/forum/03-04-2014-hasmanythrough-with-many-to-many
If you dont want relationships you can always do something like:
Order::whereHas('items.supplier', function($query) use($supplier) {
$query->where('id', $supplier->id);
});
For this to work, you need to have a relationship function items in your Order model and a relationship function supplier in your item model
I believe the reason it throws a relationship error is that you haven't created an Eloquent relation for
$supplier->supply_orders.
Instead, Laravel looks at your supply_orders() as a method in the class, and thus can't figure out which table to use as the pivot. To get the base relationship to work within Eloquent, you'd need to create a new pivot table for the relationship between suppliers and orders something like:
suppliers
-id
orders
-id
order_supplier
-id
-order_id
-supplier_id
From here, Laravel will accept a simple many to many relationship between the two (this would not cause a failure):
Supplier Class:
/**
* Get all orders associated with this supplier via order_supplier table
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function orders(){
return $this->belongsToMany("\App\Order");
}
Now that the relationship is solid both between the suppliers and orders, as well as the orders and items, you can eager load the relationship in all directions. Where it gets complicated for your particular need with the current DB setup is that you have a 3rd parameter from the items table that is not a direct pivot. Without having to re-structure the DB, I think the easiest would be to load your suppliers and the relationships like normal:
$suppliers = Supplier::with('orders', function($query) {
$query->with('items');
});
From here you've got all the relationships loaded and can draw down the ones with the right item->ids in a follow-up to the $suppliers collection. There are quite a few ways to skin the cat (even including all in one query) now that you have the Eloquent relationship... but I tend to keep it a little more simple by breaking it into a few readable bits.
Hope this helps.

Laravel Get products from multiple taxonomies

I have 3 tables, products, taxonomies and product_taxonomy. The 3rd table is a pivot table that contains product_id and taxonomy_id, meaning that product and taxonomy are many to many relationship. Given a list of taxonomy ids, how can I get all the products that belong to these taxonomies? NOTE: I want to have the products result set being able to paginated or order by maybe it's price or something.
You need to create many-to-many relationship and that requires relationship methods like:
// Product Model
public function taxonomies()
{
return $this->belongsToMany('Taxonomy');
}
// Taxonomy Model
public function products()
{
return $this->belongsToMany('Product');
}
The query:
$listOfTaxonomyIds = [1,2,3];
$products = Product::whereHas('taxonomies', function($query) use ($listOfTaxonomyIds){
$query->whereIn('taxonomy_id', $listOfTaxonomyIds);
})->get();

Resources