create list for dropdown with value from relationed table - laravel

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();

Related

How do I load a relationship from "belongsTo"?

So I have this model, 'Ticket' that contains (among other things) a "category_id" column.
To simplify, here is how my datas look:
Tickets table
id
Title
category_id
1
Issue #1
2
2
Issue #2
4
3
Issue #3
1
Categories table
id
Title
1
Authentication
2
Account
3
Billing
In my Ticket model, I've done the following:
/**
* #return Category
*/
public function category(): Category
{
return Category::firstWhere($this->category_id);
// return $this->belongsTo('App\TicketCategory'); <-- how it was, can't eager load.
}
But I'm getting an undefined function addEagerConstraints().
Edit: here is my controller:
class TicketController extends Controller
{
public function index()
{
$tickets = Ticket::with('category')->all();
// dd($tickets);
return view('tickets.index', compact('tickets'));
}
}
I also tried with hasOne but this implies that "categories" needs a "ticket_id" column, which I want to avoid.
I'd like my models just to pick the category they are related to and nothing more.
What would be a solution here?
Thanks in advance
In your category model, have the following code:
public function tickets()
{
return $this->hasMany(Ticket::class);
}
And add this in the ticket model:
public function category()
{
return $this->belongsTo(Category::class);
}
Add in the PHP 8 stuff you have already, and it should work!
Edit:
I saw you added more about how to load this properly. The code you already have in your controller should work with this as well. You'll maybe need to change the model class names in the methods I provided, but that should do the trick!
For more information about this type of relationship (one to many) read this part of the documentation.

add relationship to added values in pivot table laravel

I'm new to laravel relationships, but i'm familiar with SQL. I'm trying to add a pivot table with other values than just the two main ids.
I have some users, and some teams. Each user can play multiple instruments, and can be in multiple teams, but they can only play one instrument per team.
My pivot migration:
Schema::create('team_members', function (Blueprint $table) {
$table->integer('user_id')->unsigned();
$table->integer('team_id')->unsigned();
$table->integer('instrument_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->foreign('team_id')->references('id')->on('teams')->onDelete('cascade');
$table->foreign('instrument_id')->references('id')->on('user_instruments')->onDelete('cascade');
});
I can already see this:
$team = Team::first();
$team->members->first()->details->instrument_id; // "2"
How can I define the relationship between instrument_id on user_instruments, so I can do $team->members->first()->details->instrument
UPDATE:
I found this one: https://medium.com/#DarkGhostHunter/laravel-has-many-through-pivot-elegantly-958dd096db
Not sure if it will work...
You should create an intermediate model TeamMember, in which you will define a relationship to Instrument. To achieve that, modify your many-to-many relationship on both Team and User, adding the option "using", like this:
// Team.php
public function members() {
return $this->belongsToMany(App\User::class, 'team_members', 'user_id')
->using(App\TeamMember:class);
}
Then you need to create a model for your pivot table called TeamMember, which will extend the class Pivot and define the relationship with Instrument.
class TeamMember extends Pivot {
public function instrument() {
return $this->belongsTo(App\Instrument::class);
}
}
Then you should be able to get the instrument like this (assuming you set a custom name for your pivot table as "details"): $team->members->first()->details->instrument.

Delete second level of belonging in Eloquent

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.

Laravel 5 Eloquent Realtionship has one through

I have the following database structure
products quesionnaires questionnaire_results
-------- -------------- ----------------------
id id id
questionnaire_id questionnaire_id
product_id
A Questionnaire can be used for multiple products, and store results for each product, apart, in questionnaire_results table.
A product can have only one questionnaire and only one result of its questionnaire.
I need a way to get from Product Model the result of its Questionnaire.
If you do not have a QuestionnaireResult model then there is no straightforward Eloquent relationship for this, as all of the default relationships rely on the existence of an Eloquent model. However, there is a fairly clean way to accomplish this by treating it as a Many to Many relationship between Product and Questionnaire and treating questionnaire_results as your pivot table (even thought you really know that a product will only ever belong to one Questionnaire).
Product Model:
public function questionnaire() {
return $this->belongsToMany('App\Questionnaire', 'questionnaire_results');
}
Questionnaire Model:
public function products() {
return $this->belongsToMany('App\Product', 'questionnaire_results');
}
As this relation will return a collection, you need to either access the first array with [0] or use the first() method.
$product->questionnaire()->first();
However, if you do have a QuestionnaireResult model then you have a very straightforward One to One relationship with no need to go through another model. You have some questionnaire results that belong to a product, while a product can only have one set of questionnaire results (according to your post info).
Product Model:
public function questionnaireResult() {
return $this->hasOne('App\QuestionnaireResult');
}
Questionnaire Result Model:
public function product() {
return $this->belongsTo('App\Product');
}
// Product Model
public function quesionnaire() {
return $this->hasOne("App\Quesionnaire");
}
// Quesionnaire Model
public function questionnaire_result() {
return $this->hasOne("App\Questionnaire_result");
}
$product->quesionnaire->questionnaire_result

Laravel - Pivot table for three models - how to insert related models?

I have three models with Many to Many relationships: User, Activity, Product.
The tables look like id, name. And in the each model there are functions, for example, in User model:
public function activities()
{
return $this->belongsToMany('Activity');
}
public function products()
{
return $this->belongsToMany('Product');
}
The pivot table User_activity_product is:
id, user_id, activity_id, product_id. The goal is to get data like: User->activity->products.
Is it possible to organize such relations in this way? And how to update this pivot table?
First I suggest you rename the pivot table to activity_product_user so it complies with Eloquent naming convention what makes the life easier (and my example will use that name).
You need to define the relations like this:
// User model
public function activities()
{
return $this->belongsToMany('Activity', 'activity_product_user');
}
public function products()
{
return $this->belongsToMany('Product', 'activity_product_user');
}
Then you can fetch related models:
$user->activities; // collection of Activity models
$user->activities->find($id); // Activity model fetched from the collection
$user->activities()->find($id); // Activity model fetched from the db
$user->activities->find($id)->products; // collection of Product models related to given Activity
// but not necessarily related in any way to the User
$user->activities->find($id)->products()->wherePivot('user_id', $user->id)->get();
// collection of Product models related to both Activity and User
You can simplify working with such relation by setting up custom Pivot model, helper relation for the last line etc.
For attaching the easiest way should be passing the 3rd key as a parameter like this:
$user->activities()->attach($activityIdOrModel, ['product_id' => $productId]);
So it requires some additional code to make it perfect, but it's feasible.
The solution was found with some changes.
In the models relationships look like:
// User model
public function activities()
{
return $this->belongsToMany('Activity', 'user_activity_product', 'user_id', 'activity_id')->withPivot('product_id');
}
public function products()
{
return $this->belongsToMany('Product', 'user_activity_product', 'user_id', 'product_id')->withPivot('activity_id');
}
To update pivot table:
$user->products()->save($product, array('activity_id' => $activity->id));
- where product and activity ids I get from Input.
And, for example, to check if "user -> some activity -> some product is already exists":
if ($company->activities->find($activity_id)->products()->where('product_id', '=', $product_id)->wherePivot('company_id', $company_id)->get()->count() > 0) {
// code...
}
I think it needs improvements but it works for me now.

Resources