Using relations on a collection - laravel

I have a Collection that holds the root category and all descendants. In my Category model, I have established that there can be many posts in relation to the category. I retrieve the category and it's descendants with this code:
$category = Category::findOrFail($categoryID);
$categoryAndDescendants = $category->getDescendantsAndSelf();
$categoryAndDescendants is a Collection object that holds Category models. Is it possible to retrieve all of the posts at once?
I basically want to do something like:
$posts = $categoryAndDescendants->posts()->orderBy('timestamp', 'DESC');
Which would retrieve all of the posts for all of the categories and their descendants in that particular collection.
Thanks for the help and I apologise for the awful wording.

I think that's not possible.
But you could write a custom Collection, and implements this function. Something like this:
<?php
use Illuminate\Support\Collection;
class CategoryCollection extends Collection
{
public function posts()
{
$posts = new Collection();
foreach ($this->items as $category) {
foreach ($category->posts() as $post) {
$posts->add($post);
}
}
return $posts;
}
}
And then, you just need to set this custom collection to your Category model.
class Category extends Eloquent
{
public function newCollection(array $models = array())
{
return new CategoryCollection($models);
}
}

Related

how to retrive posts where their category is same with current post in laravel?

I have post and category many to many relationship where post and category model define correctly, I have pivot table to but no model for pivot table.
When I get one post I have the post categories to, $post->categories.
I want to list all post with current post categories, for exapmle if the current post have category of A and B, I want to retrieve all posts with category of A and B.
What is best and easiest way in laravel elequent for this purpose?
here my models if it help
class Post extends Model
{
public function categories(){
return $this->belongsToMany(Category::class);
}
}
class Category extends Model
{
public function posts(){
return $this->belongsToMany(Posts::class);
}
}
public function singlePost(Post $post){
$postsWithSameCategory = ???
return view('home.post',compact('post'));
}
If you want the posts with the same categories
From your example; posts with categorie A, B, A/B, A/B/C would be returned
public function singlePost(Post $post){
$postsWithSameCategory = Post::whereHas('categories', function($query) use ($post) {
$query->whereIn('id', $post->categories->pluck('id'));
})->get();
return view('home.post',compact('post', 'postsWithSameCategory'));
}
If you want the posts with the exact same categories
From your example; Only posts with categorie A/B would be returned
public function singlePost(Post $post){
$postsWithSameCategory = Post::whereHas('categories', function($query) use ($post) {
$query->whereIn('id', $post->categories->pluck('id'));
}, '=', $post->categories->count())->get();
return view('home.post',compact('post', 'postsWithSameCategory'));
}
To also answer your comment, you can define a scope your Post model
public function scopeWhereIsRelatedWithPost($query, $post)
{
return $query->whereHas(/** ... **/);
}
$posts = Post::whereIsRelatedWithPost($post)->get();

How to write this same code and get same data in eloquent relationship ... laravel

How to write this same code and get same data in eloquent relationship
public function index(){
$data = DB::table('categories')
->join('subcategories', 'categories.id', 'subcategories.cat_id')
->select('categories.*', 'subcategories.*')->paginate(5);
return view ('admin.subcategory.index', compact('data'));
}
You need to create hasMany relationship between Category and Subcategory.
Category model
class Category extends Model
{
public function subcategories()
{
return $this->hasMany('App\Subcategory','cat_id','id');
}
}
Now you can call with('subcategories')
Category::with('subcategories')->paginate(5)

hasOne Relation not working in laravel

Hi I want to make relationship between two tables first Table is product and second is productimages
ProductController.php
public function product(){
$products = Product::all();
dd($products->images);
}
Product.php (modal)
class Product extends Model
{
public $timestamps = false;
//
public function images()
{
return $this->hasOne(ProductImage::class);
}
}
ProductImage.php(model)
class ProductImage extends Model
{
public function product(){
return $this->belongsTo(Product::class);
}
}
When i am using this method $products = Product::find(1); working find but i need all.
Thanks
When you're doing $products->images you're trying to access property of collection.
Preload all products with their images, use with() method:
$products = Product::with('images')->get();
Then you'll be able to get image of each product and avoid N + 1 problem:
#foreach ($products as $product)
// Access $product->image here
#endforeach
Product::with('images')->get();

How do I get all children that fall under a parent in eloquent?

In my database, I have a Categories table. Categories can have parent categories, making it a recursive relationship
I also have a products table. Each product falls under a category.
Say, for example, I have a tree that looks like this:
Category
Sub-Category 1
Sub-Sub-Category 1
Product 1
Product 2
Product 3
Product 4
Sub-Sub-Category 2
Product 5
Product 6
Product 7
Product 8
Sub-Category 2
Sub-Sub-Category 3
Product 9
Product 10
Product 11
Product 12
If I do $SubCategory1->products, I want it to give me Products 1-8
If I do $SubSubCategory3->products, I want it to give me products 9-12
If I do $Category->products, I want it to give me all products
Basically, I want the category to give all products that fall under it
After hoping to find an answer that uses Laravel nicely, I ended up giving up and just writing the code to do what I wanted myself, and it ended up smaller than I anticipated.
public function all_products()
{
$products = [];
$categories = [$this];
while(count($categories) > 0){
$nextCategories = [];
foreach ($categories as $category) {
$products = array_merge($products, $category->products->all());
$nextCategories = array_merge($nextCategories, $category->children->all());
}
$categories = $nextCategories;
}
return new Collection($products); //Illuminate\Database\Eloquent\Collection
}
This way works very good:
class One extends Model {
public function children()
{
return $this->hasMany(self::class, 'parent_id');
}
public function grandchildren()
{
return $this->children()->with('grandchildren');
}
}
Suppose your Model name is Category
Create a function on Category model
public function children() { return $this->hasMany('App\Category', 'parent_id', 'id'); }
Using above method on your controller
$categories = Category::with('children')->where('parent_id',0)->get();
please try the below Has Many Through relation and post the result
class Category extends Model
{
public function products()
{
return $this->hasManyThrough(
'App\Product', 'App\Category',
'parent_id', 'catergory_id', 'id'
);
}
}
Then you can use $category->products; to find your products
For anyone that is searching for this answer here is a simple solution:
class Group extends Model
{
public function ancestry()
{
return $this->belongsTo('App\Group', 'parent_id')->with('ancestry');
}
public function descent()
{
return $this->hasMany('App\Group', 'parent_id')->with('descent');
}
}
ancestry function will give you the path to root and descent will give you all the descendence of that node.
you better use nested sets models. you should use this data structure due to get fastest way to query in mysql with a simple way. with just a parent_id attribute,you don't know how deep your tree is and this make problem in knowing how many join you need.
I offer you this excellent article. managing-hierarchical-data-in-mysql
for those who use laravel framework, I prefer to suggest this amazing package:Baum
https://stacklearn.ir
ProductCategory.php
public function descent()
{
return $this->hasMany('App\ProductCategory', 'parent_id')->with('descent');
}
Controller
$categories = ProductCategory::with('descent')->get();
Desired Tree Was:
A simple category tree
Output of $categories variable:
Output in Array Format
Say $category_id = Category->id and you want a collection of products as children of that category, I would try:
$products = App\Product::with(['SubSubCategory.SubCategory.Category'=>function($query) use ($category_id) {$query->where('id', $category_id);}])->get();
To be able to do so. You will need your 'one to many' inverse relationships to be as such:
//App\SubCategory
public function Category(){return $this->belongsTo('App\Category');}
//App\SubSubCategory
public function Sub_Category(){return $this->belongsTo('App\SubCategory');}
//App\Product
public function SubSubCategory(){return $this->belongsTo('App\SubSubCategory');}
Good luck.
i made managed_by in table (users) and this solution get all unlimited levels of children .
in User Model
public function Childs(){
return $this->hasMany('App\User', 'managed_by', 'id')->with('Childs');
}
in helpers file (My magic Solution )
if (!function_exists('user_all_childs_ids')) {
function user_all_childs_ids(\App\User $user)
{
$all_ids = [];
if ($user->Childs->count() > 0) {
foreach ($user->Childs as $child) {
$all_ids[] = $child->id;
$all_ids=array_merge($all_ids,is_array(user_all_childs_ids($child))?user_all_childs_ids($child):[] );
}
}
return $all_ids;
}
}
In Category model
protected static $childrenIds = [];
/**
* get ids children categories with self id
* #return array
*/
public function getChildrenIdsAttribute(): array
{
self::$childrenIds[] = $this->id;
if ($this->has_children) {
foreach ($this->children()->active()->get() as $child) {
$child->children_ids;
}
}
return self::$childrenIds;
}
public function getHasChildrenAttribute()
{
return (bool)$this->children->count();
}
public function scopeActive($query)
{
return $query->where('is_active', '=', true);
}
public function children(): HasMany
{
return $this->hasMany(self::class, 'parent_id');
}
Route
Route::get('category/{category}/products', [ProductController::class, 'categoryProducts'])
Controller
public function categoryProducts(Category $category): JsonResponse
{
$product = Product::whereIn('category_id', $category->children_ids)->get();
return response()->json($product);
}

How do I query an Eloquent many-to-many relationship to see if a model instance is in a collection?

My Eloquent models are described below:
class Product extends Eloquent{
...
public function categories(){
return $this->belongsToMany('Category');
}
...
}
class Category extends Eloquent{
...
public function products(){
return $this->belongsToMany('Product');
}
...
}
How do I write a cleanly structured query for whether a given product is in a category?
Extra love if it can be a method of the class!
My closest (and very messy) attempt is something like:
$categories = Category::all();
$product = Product::find($id);
$id = 3;
foreach($categories as $cat)
while($cat->products())
if($product->id === $cat->products()->id)
true
endif
endwhile
endforeach
The easiest way to check, whether the relation exists, would be:
$product = Product::whereHas('categories', function ($q) use ($categoryId) {
$q->where('categories.id', $categoryId);
})->findOrFail($productId);
This will return the product instance or throw ModelNotFoundException if it was not found (meaning no product for given id or the product has no relation with the given category).
// or:
$product = Product::find($id);
$product->categories()->where('categories.id', $categoryId)->exists(); // true/false
You can also check this one for more clues: MySQL nested resources (pivot tables) permission to view/update/manage
Also you can use contains on the collection:
$categories = Category::with('products)->get();
$product = Product::find($id);
foreach ($categories as $category)
{
if ($category->products->contains($product)
{
// do something
}
}
// note:
$category->products; // Eloquent Collection object
$category->products(); // BelongsToMany relation object

Resources