Laravel scope on collection - php

I'm in Pain because of this problem.
Problem
I have categories table which contains id, slug and parent_id.
lessons table which contains category_id (the child of a Category)
I want by Eloquent scope get all lessons posted by Category parent id
example:
parent category of id=1 have children categories of ids 3, 4 and 5.
Then retrieve Lessons under the parent category of id=1, so this must return the lessons of all of the childrens.
Categories table
public function parent()
{
return $this->belongsTo(self::class, 'parent_id');
}
public function lessons()
{
return $this->hasManyThrough(
\App\Lesson::class, \App\Category::class,
'parent_id', 'category_id', 'id'
);
}
//Pointless try
// public function scopeGetParent($query, $slug)
// {
// $query->where('slug', $slug)->first()->lessons;
// }
public function children()
{
return $this->hasMany(self::class, 'parent_id');
}
Lessons.php
return Lesson::recentLessons()
->byParentCategory($args['category'])
->simplePaginate($page)
->shuffle();
how can I construct scopeByParentCategory() function??
sorry for the fuzzy question! Spent 3 hours trying :(
update: I got this idea right after posting the question but still wanna know what do you think about it and if this the best way to handle it.
public function scopeByParentCategory($query, $slug)
{
$ids = [];
foreach (Category::where('slug', $slug)->first()->children as $value)
{
$ids[] = $value->id;
}
return $query->whereIn('category_id', $ids);
}

First put this code inside of your Category model:
public function scopeOfSlug($query, $slug)
{
$query->where('slug', $slug);
}
Then in your controller
return Category::ofSlug($args['category'])
->lessons
->simplePaginate($page)
->shuffle();

Related

Laravel has and orHas not working with where

I need get categories with products, only where products exists on category, I have code:
$categories = Category::with(['children.products', 'products'])->has('children.products')->orHas('products')->whereNull('parent_id')->whereId($category->id)->paginate(15);
But whereId = $category->id not working.. Why? I get category with other categories.. but I need get only certain category, where id = categoryId
Model category:
public function children()
{
return $this->hasMany(self::class, 'parent_id');
}
public function parent()
{
return $this->belongsTo(self::class, 'parent_id')->withoutGlobalScope('active');
}
public function products()
{
return $this->hasMany(Product::class);
}
You should combine has and orHas within enclosed function, something similar to below:
$query->where(function($query) {
$query->has('children.products');
$query->orHas('products');
});
Additionally, to above, I would recommend using "join" because "has" is a lot slower.

How to get Category info in detail page in laravel

I have a page where i show products under their subcategory url's and in top of my page i want to print the subcategory title and subcategory image, how can I do that?
here is my function:
public function productsubcategory($slug, $subslug)
{
$products = Product::whereHas('subcategory', function($q) use($subslug){
$q->where('slug',$subslug);
})->paginate(12);
return view('frontend.subcategories', compact('products'));
}
and I show my products with foreach like #foreach($products as $product) ....
The best way you doing is One to Many relationship. Use ORM for that. You will get details here: https://laravel.com/docs/5.0/eloquent
In your Category.php Model
public function subcategories()
{
return $this->hasMany('App\Subcategory', 'category_id');
}
In you Subcategory.php Model
public function category()
{
return $this->belongsTo('App\Category', 'category_id');
}
public function products()
{
return $this->hasMany('App\Product', 'subcategory_id');
}
And your Product.php Model
public function subcategory()
{
return $this->belongsTo('App\Subcategory', 'subcategory_id');
}
And you will get subcategory list
$subcategories = Category::find($id)->subcategories;
And You get products
foreach($subcategories as $subCat)
{
//your code
}
And here is another link for One to Many relationship. If you read details it will help you. https://laravel.com/docs/5.5/eloquent-relationships
SOLVED
I added another query in my function so totally become like this:
public function productsubcategory($slug, $subslug)
{
$products = Product::whereHas('subcategory', function($q) use($subslug){
$q->where('slug',$subslug);
})->paginate(12);
$productss = Product::whereHas('subcategory', function($q) use($subslug){
$q->where('slug',$subslug);
})->first();
return view('frontend.subcategories', compact('products', 'productss'));
}
And here is how i show my subcategory info in their pages.
{{$productss->Subcategory->title}}
Hope this help others.

Hide unpublished items from counter in Laravel query

I'm showing all sub categories under main category. On each sub-category there is a counter of how many items are assigned there.
Main Category
- Sub Category (1)
- Sub Category (3)
- etc
Current problem is that one item can be published and unpublished. When the item is not published yet I don't want to show it on counter. Column in items table is published and accept 1 for published and 0 for unpublished.
This is what I have to show them on page
HomeController.php
$allCategories = Category::where('parent_id', 0)->has('children.item')
->with(['children'=> function($query){
$query->withCount('item');
}])
->get()
->each(function($parentCategory){
$parentCategory->item_count = $parentCategory->children->sum(function ($child) { return isset($child->item_count)?$child->item_count:0;});
});
My Item.php model
public function category()
{
return $this->belongsTo('App\Category');
}
My Category.php model
public function item()
{
return $this->hasMany('App\Item','category_id');
}
public function parent()
{
return $this->belongsTo('App\Category', 'id', 'parent_id');
}
public function children()
{
return $this->hasMany('App\Category', 'parent_id', 'id');
}
I've tried to directly add it to the query but doesn't make any difference
$query->withCount('item')->where('published', 1);
You have to add the where condition in the relationship function item of Category.php model.
Here is the code:
public function item()
{
return $this->hasMany('App\Item','category_id')->where('published', 1);
}

Laravel: Ordering data at a query level when using pivot tables?

In my routes/web.php I have a route like this...
Route::get('/tags/{tag}', 'TagsController#show');
Then, inside TagsController because I have a post_tag pivot table that has been defined as a many-to-many relationship.
Tag.php...
public function posts(){
return $this->belongsToMany(Post::class);
}
public function getRouteKeyName(){
return 'name';
}
Post.php...
public function tags(){
return $this->belongsToMany(Tag::class);
}
I get the posts for a certain tag like this...
public function show(Tag $tag){
$posts = $tag->posts;
return view('posts.index', compact('posts','tag'));
}
Then, to sort the posts into newest first I can do this in index.blade.php...
#foreach ($posts->sortByDesc('created_at') as $post)
#include('posts.post')
#endforeach
This works fine, but I'm doing the re-ordering at collection level when I'd prefer to do it at query level.
From Eloquent: Relationships I can see that I can do something like this, which also works...
$user = App\User::find(1);
foreach ($user->roles as $role) {
//
}
But, something like this does not seem to work...
public function show($tag){
$posts = \App\Tag::find($tag);
return view('posts.index', compact('posts'));
}
My question is, how can I filter/order the data at a query level when using pivot tables?
To order your collection you must change
public function tags(){
return $this->belongsToMany(Tag::class);
}
to
public function tags(){
return $this->belongsToMany(Tag::class)->orderBy('created_at');
}
Extending #leli. 1337 answer
To order content without changing the relation created.
First, keep the original relation
class User
{
public function tags
{
return $this->belongsToMany(Tag::class);
}
}
Second, during query building do the following
//say you are doing query building
$users = User::with([
'tags' => function($query) {
$query->orderBy('tags.created_at','desc');
}
])->get();
With this, you can order the content of tags data and in query level also if needed you can add more where clauses to the tags table query builder.

Laravel relation returning empty

I want to get all the books under a certain category. The category has many subjects and books are linked to subjects.
My code:
class BookCategory extends Model {
public function subjects() {
return $this->hasMany('BookSubject', 'category_id', 'id');
}
public function getBooksAttribute() {
return Books::whereHas('subjects', function ($query) {
return $query->join('book_category', 'book_category.id', '=', 'book_subject.subject_id')
->where('book_category.id', $this->id);
})->get();
}
}
and my Books model:
class Books extends Model {
public function subjects() {
return $this->belongsToMany('BookSubject', 'book_by_subject', 'book_id', 'subject_id');
}
}
If I do:
$cats = \App\Models\BookCategory::all();
foreach ($cats as $c) {
echo $c->books->count();
}
It's always returning 0 for all the rows. What I'm I doing wrong?
I believe the problem is in your subquery:
return $query->join('book_category', 'book_category.id', '=', 'book_subject.subject_id')
->where('book_category.id', $this->id);
I'm pretty sure book_subject.subject_id should be book_subject.category_id
Hard to tell without seeing your db schema.

Categories