Delete second level of belonging in Eloquent - laravel

My application has a Category model. Each Category can have many Subcategories, so each Subcategory belongsTo a Category. From the Subcategory model:
public function category()
{
return $this->belongsTo('App\Category');
}
On the other hand, there's a Friend model. Now, each Friend/Subcategory pair is connected through the pivot friend_score table:
id | friend_id | subcategory_id | score
From the Subcategory model:
/**
* The friend scores that belong to the subcategory.
*/
public function friends()
{
return $this->belongsToMany('App\Friend', 'friend_score')->withPivot('score');
}
From the Friend model:
/**
* The subcategory scores that belong to the friend.
*/
public function subcategories()
{
return $this->belongsToMany('App\Subcategory', 'friend_score')->withPivot('score');
}
When I delete a subcategory, Eloquent automatically, and correctly, deletes the entries from the friend_score table for that subcategory_id.
When I delete a category, it correctly deletes the subcategories that belong to that category_id. However, in this case, the related friend_scores remain in the database.
How do I make it delete the friend_score for each child subcategory when deleting a category?
I'm aware I could manually iterate through them and delete them, but I'm wondering if there is a way to make this happen automatically through model relationships.

In your friend_score migration, You can define subcategory_id as foreign key. Then you can choose what will happen to pivot records with onDelete() method when sub category deleted.
$table->foreign('subcategory_id')
->references('id')
->on('subcategories')
->onDelete('cascade');
With cascade option, when sub category deleted, all related pivot records will removed..
So when you deleted a category, deleting will bubble to friend scores through sub category
category-> subcategory -> friend scores (pivot)

You can update the migration and add onDelete() cascading, which will be my preferred way of handling such cases. But if you are really looking to do this manually, you can use eloquent's events like follows :
Subcategory Model :
/**
* Boot eloquent model events
*
* #return void
*/
public static function boot() {
parent::boot();
// This will be called just before deleting a
// subcategory entry
static::deleting(function($subcategory) {
Friend::where('subcategory_id', $subcategory->id)->delete();
});
}
Before doing any of it, you can do a cleanup by following :
Friend::whereDoesntHave('subcategories')->get();
Check if above returns correct result of entries which does not have any relation of subcategories table. Then you can call ->delete() instead of ->get() to clean up the friend model entries.

Related

Laravel getting data from a deep relationship

I have 3 tables:
Users
Baskets
Items
id
id
id
user_id
basket_id
price
I'm trying setup a relationship in eloquent by which I can get all the all the Items and the corresponding Baskets where the price of items is X. I want it so that I can simply use $user->items(x) and get the results.
I'm unsure if this can be done using Relationships alone or will I have to resort to writing custom queries.
Any, and all, help and guidance will be appreciated!
The relationship you are looking for is hasManyThrough
https://laravel.com/docs/7.x/eloquent-relationships#has-many-through
User Modal
public function items()
{
return $this->hasManyThrough(Item::class, Bucket::class);
}
The way you want to use is not possible to achieve I think.
Possible Usage
$user->items()->where('price', x);
of if you define custom scopes
Item Modal
public function scopeWherePrice($query, $value)
{
return $query->where('price', $value);
}
Usage
$user->items()->wherePrice(x);
EDIT
If you really want to write a code like $user->items(x) you can define a method on the User Modal.
Note that this is not a relationship, just another method which fetch the result.
User Modal
public function items($price)
{
return this->items()->where('price', $price)->get();
}
Using hasManyThrough Define the relationship in your models:
User Model
/**
* Get all of the items & baskets for the user.
*/
public function items($price)
{
return $this->hasManyThrough('App\Items', 'App\Baskets')
->with('basket')
->where('price',$price);
}
Basket Model
/**
* Get the Items's Basket.
*/
public function basket()
{
return $this->belongsTo('App\Basket','basket_id');
}
And then call it like this:
$user->items(x);
This will return all user items with their corresponding baskets in a specific price.

How to populate category table that is linked with product table in laravel

I have two mysql tables namely category and products.
I have created add.blade.php and adding new products are working well. But I am facing difficulty in editing category name and listing the products with category name. and I do not know which model relationship to use as editing involves in one to one and listing involves in one to many.
category table
products table
product model
category model
controller edit function
controller index(listing) function
Your products belong to a category so you need a category_id column on your products table.
The relationships would be as simple as
Product.php
public function category()
{
return $this->belongsTo(Category::class);
}
Category.php
public function products()
{
return $this->hasMany(Product::class);
}
This is real basic laravel, you should read the documentation and stop asking questions like this on here

Retrieve posts that belong to a list of categories laravel

I need your help to find a solution to create this query. Imagine I have 2 tables: posts and categories, and a pivot table: category_post.
posts: id, title, ...
categories: id, category_name, ...
category_post: post_id, category_id
Now, I am going to retrieve the categories I want:
$categories = Category::whereIn('id', $array)->lists('id');
After this query I am having a list of category ids and I want to retrieve only the posts that belong to those categories. Not only the posts that belong to one category or another but only the posts that belong to all of those categories.
I hope I explaned myself well.
Regards
You can register a belongTo relation for the pivot class. (provided category_id and post_id are foreign keys.)
class CategoryPost extends Eloquent {
protected $table = 'category_post';
public function posts() {
return $this->belongsTo('posts');
}
public function categories() {
return $this->belongsTo('categories');
}
}
Now, you can call
CategoryPost::with('posts')->whereIn('category_id', $categories)->groupBy('post_id')->get();

create list for dropdown with value from relationed table

My model Product return something like this
id = 1
name = Product Name
category_id = 101
category = array
id = 101
name = electronics
and I need to use lists() to create something like this:
lists("category.name", "id")
but it looks that this is impossible. How to create list for dropdown like this:
1 = electronics
2 = furniture
3 = cars
...
thanks a lot
Are you sure you did set up the right relations? Cause that is just what lists() usually does and it's far from impossible to create such a list.
It's quite hard to understand how you did design your Eloquent logic without your code, but here's how I would handle your situation, assuming you've already set up the category_product pivot table.
Given two models, Product and Category, I'd define these 2 relations cause, as far as I can get, a product can have several categories and a categories can be associated with several product :
class Product extends Model
{
////////
public function categories()
{
return $this->belongsToMany('App\Category');
}
////////
}
and
class Category extends Model
{
////////
public function products()
{
return $this->belongsToMany('App\Product');
}
////////
}
At this point, you're good to go, all you have to do in order to get your list is
Product::findOrFail($id)->categories->lists('name','id')
This will return an array holding all the categories associated with the given product
array:[
1 => first-category
2 => second-category
3 => third-category
....
]
Note that with this set up it'll work the other way around as well, if you want to get a list of all the product that match a given category
Category::findOrFail($id)->products->lists('name','id')
At the very top of the answer I assumed you did set up a pivot table but, if you didn't, here's a quick tip:
within your products migration
public function up()
{
Schema::create('products', function (Blueprint $table) {
//
});
Schema::create('category_product', function (Blueprint $table){
$table->integer('category_id')->unsigned()->index();
$table->foreign('category_id')->references('id')->on('categories')->onDelete('cascade');
$table->integer('product_id')->unsigned()->index();
$table->foreign('product_id')->references('id')->on('products')->onDelete('cascade');
$table->timestamps();
});
}
Then migrate and now everything is properly set up. Last but not least, if you are going to use this migration, you'll need to fix the relation in the model, which one is up to yours app logic: let's say the flow is create a new product -> choose the categories within the creation form, then you need to add timestamps() in the categories method
return $this->belongsToMany('App\Category')->withTimeStamps();

How to model these relashionships?

Well, these are two simple entities but with many interactions which makes me a bit confused about how to do the relationship setup ..
So, I have two entities .. Category and Article .. It seems simple.. but here are what I need to do:
Categories can be a standalone model with no relationships
Category can contain other categories
Categories can contain Articles
Articles can be standalone (not inside categories)
How do you think I can model these entities and relationships between them?
Most straightforward:
// categories table: category_id fk categories.id nullable
// Category model
public function parent()
{
return $this->belongsTo('Category', 'category_id');
}
public function children()
{
return $this->hasMany('Category', 'category_id');
}
public function articles()
{
return $this->hasMany('Article');
}
// articles table: category_id fk categories.id nullable
// Article model
public function category()
{
return $this->belongsTo('Category');
}
Mind that if you wish to build a tree of those categories and then load whole tree, this will not be easy. If that's the case you should use eg. adjacency list or other model for self-referencing tables.

Resources