Eloquent Relationship without Pivot Model - php

I have 3 tables:
customer: fields: id, name
item: fields: id, name
customer_items: fields: customer_id, item_id, qty
Customer and Item have their separate Models as we would expect.
Question: How would I relate these two(Customer and Item) without having a pivot model.
I want to directly get customer items using $customer->items instead of doing $customer->customerItem->items which I find unnecessary since I don't want to track customerItems & customer by item.
Also, I cannot directly use customer_items table for Item model as I might need to retrieve all items in its controller.

As pointed out by #kerbholz (but they didn't create an answer so here it is), in your customer model you need the following function:
public function items()
{
return $this->belongsToMany('App\Item');
}
Which assumes your Item model class sits within App. You may also do the reverse in your Item model.
Now you should be able to do $customer->items and get a collection of items.
If you want to include the qty field then you need:
public function items()
{
return $this->belongsToMany('App\Item')->withPivot('qty');
}
Note that you still need the pivot table, you can't escape that, but you can now navigate it in a more elegant manner.

creat customer_items table and include custmer_item_id and user_id . in User model include this function
public function basket() // example
{
return $this->belongsToMany(User::class, 'customer_items', 'user_id', 'custmer_item_id');
}

Related

Retrieve Parent Model Through Pivot Table Laravel

I'm currently struggling with retrieving data towards a parent model. I'll drop my database, classes, and things I've tried before.
I have 4 tables: sales_orders, products, work_orders, and product_sales_order (pivot table between sales_orders and products).
SalesOrder.php
class SalesOrder extends Model
{
public function products()
{
return $this->belongsToMany(Product::class)
->using(ProductSalesOrder::class)
->withPivot(['qty', 'price']);
}
}
ProductSalesOrder.php
class ProductSalesOrder extends Pivot
{
public function work_orders()
{
return $this->hasMany(WorkOrder::class);
}
public function getSubTotalAttribute()
{
return $this->qty* $this->price;
}
}
WorkOrder.php
class WorkOrder extends Model
{
public function product_sales_order()
{
return $this->belongsTo(ProductSalesOrder::class);
}
public function sales_order()
{
return $this->hasManyThrough(
ProductSalesOrder::class,
SalesOrder::class
);
}
}
So, what I want to retrieve sales order data from work order since both tables don't have direct relationship and have to go through pivot table and that is product sales order. I've tried hasOneThrough and hasManyThrough but it cast an error unknown column. I understand that error and not possible to use that eloquent function.
Is it possible to retrieve that sales order data using eloquent function from WorkOrder.php ?
You cannot achieve what you want using hasOneThrough as it goes from a table that has no ID related to the intermediate model.
In your example you are doing "the inverse" of hasOneThrough, as you are going from a model that has the ID of the intermediate model in itself, and the intermediate model has the ID of your final model. The documentation shows clearly that hasOneThrough is used exactly for the inverse.
So you still should be able to fix this, and use a normal relation as you have the sales_orders_id in your model SuratPerintahKerja, so you can use a normal relation like belongsTo to get just one SalesOrder and define it like this:
public function salesOrder()
{
return $this->belongsTo(SalesOrder::class, 'sale_orders_id');
}
If you want to get many SalesOrders (if that makes sense for your logic), then you should just run a simple query like:
public function salesOrders()
{
return $this->query()
->where('sale_orders_id', $this->sale_orders_id)
->get();
}
Have in mind that:
I have renamed your method from sales_order to salesOrder (follow camel case as that is the Laravel standard...).
I have renamed your method from sales_order to salesOrders for the second code as it will return more than 1, hence a collection, but the first one just works with one model at a time.
I see you use sale_orders_id, but it should be sales_order_id, have that in mind, because any relation will try to use sales_order_id instead of sale_orders_id, again, stick to the standards... (this is why the first code needs more parameters instead of just the model).
All pivot tables would still need to have id as primary and auto incremental, instead of having the id of each related model as primary... Because in SuratPerintahKerja you want to reference the pivot table ProdukSalesOrder but it has to use both produks_id (should have been produk_id singular) and sale_orders_id (should have been sales_order_id). So if you were able to use something like produk_sales_order_id, you could be able to have better references for relations.
You can see that I am using $this->query(), I am just doing this to only return a new query and not use anything it has as filters on itself. I you still want to use current filters (like where and stuff), remove ->query() and directly use the first where. If you also want to add ->where('produks_id', $this->produks_id) that is valid and doesn't matter the order. But if you do so, I am not sure if you would get just one result, so ->get() makes no sense, it should be ->first() and also the method's name should be salesOrder.
Sorry for this 6 tip/step, but super personal recommendation, always write code in English and do not write both languages at the same time like produks and sales orders, stick to one language, preferrably English as everyone will understand it out of the box. I had to translate some things so I can understand what is the purpose of each table.
If you have any questions or some of my code does not work, please tell me in the comments of this answer so I can help you work it out.
Edit:
After you have followed my steps and changed everything to English and modified the database, this is my new code:
First, edit ProductSalesOrder and add this method:
public function sales_order()
{
return $this->belongsTo(SalesOrder::class);
}
This will allow us to use relations of relations.
Then, have WorkOrder as my code:
public function sales_order()
{
return $this->query()->with('product_sales_order.sales_order')->first();
}
first should get you a ProductSalesOrder, but then you can access ->sales_order and that will be a model.
Remember that if any of this does not work, change all the names to camelCase instead of kebab_case.

Laravel table relationship to pivot table

I'm confused on how to get my model setup in laravel with a table that is connected to a pivot table.
Here's the problem
Say I have
Locations
id
name
area_types
id
name
area_type_location (Pivot table)
id
location_id
area_type_id
area_test
id
area_type_location_id
clean
headCount
Relationship between the tables are different areas type belongs to different locations.
i.e: beach, 25m pools, kids pools, bbq, etc
area_test is connected to the pivot table because the test has to be generated from area that exists, in this case it is area that is registered under different locations. Thus it has to be tested daily, measured, etc.
I understand the structure between area_types and locations for many to many relationship, however I can't get over my head of how do i structure my area_test model? How do I get the data from locations table -> where are my test?
Should I create a model for my pivot table? Is that a good practice in laravel?
Does anyone has the same use case?
I read about eloquent has many through
relationship but I understand that it does not mention about getting through pivot table. I don't quite get if my use case is the same.
Thanks
Finally, apparently there are a couple of way to get data from locations table to area_tests
Tried at tinker and it works,
First Option
I need to create a Pivot model for my Pivot table:
class LocationAreaType extends Pivot{
public function location(){
return $this->belongsTo(Location::class);
}
public function areaType(){
return $this->belongsTo(AreaType::class);
}
public function AreaTests(){
return $this->hasMany(AreaTest::class, 'area_type_location_id');
}
}
I can use hasManyThrough relation that I need to create in my Location table
public function areaTests()
{
return $this->hasManyThrough(
AreaTest::class,
LocationAreaType::class,
'location_id',
'area_type_location_id');
}
this way I can get the areaTests easily by $location->areaTests, My problem was not determining the area_type_location_id as foreign. You need to determine this, apparently when I extends pivot and use hasMany laravel does not auto recognise the Foreign key by itself.
Second option
Another way to access it is from the relation table, I can define withPivot in the areaTypes() relation then access it like this:
$location->areaType[0]->pivot->areaTests
Since laravel only recognise foreign key from both tables location_id and area_type_id, I have to include the id of the pivot table to get the AreaTest table data
So in the Location model I have to get the column
public function areaTypes()
{
// Get the ID of the pivot table to get the poolTests table (connected with ID column)
return $this->belongsToMany(AreaType::class)
->using(AreaTypeLocation::class)
->withPivot('id');
}
There is no need to create a new model for pivot table.
Just declare in Location model below code:
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function area_types()
{
return $this->belongsToMany('App\AreaType', 'area_type_location', 'location_id', 'area_type_id');
}
and declare below code in AreaType model:
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function locations()
{
return $this->belongsToMany('App\Location', 'area_type_location', 'area_type_id', 'location_id');
}
every time you need to get for example the locations of an area_type in every controller, you can call the function like this: $areatype->locations()->get();
Don't forget to create area_type_location table migration.

How to create a correct relationship between Laravel models

So I have two tables named 'customers' and 'billing_addresses'.
I also have two models named 'Customer' and 'BillingAddress'.
In the billing_addresses table, there are multiple addresses, each having an 'id'.
In the customers table, each customer has an 'billing_address_id' value corresponding to one of the entries in the billing_addresses table.
What I managed to create so far is:
// Customer relationship method(in the model file)
public function address()
{
return $this->hasOne('App\BillingAddress','id');
}
/////////
// BillingAddress relationship method(in the model file)
public function customer()
{
return $this->hasMany('App\Customer','id');
}
I am indeed getting the right data when I do something like Customer::with('address');
but I feel like there is a better approach for all this as I'm also getting
BadMethodCallException
Call to undefined method
Illuminate\Database\Eloquent\Relations\HasOne::getForeignKey()
when I try to filter with datatables(can't tell if its related but first I'd want to have the best approach on the relationships).
Thank you!
What you want to achieve is this:
// Customer Model
public function address()
{
return $this->belongsTo('App\BillingAddress');
}
// BillingAddress Model
public function customer()
{
return $this->hasMany('App\Customer');
}
You need the belongsTo on your Customer Model because you have the Billing id stored within the Customer table. Eloquent will automatically match the billing id to the foreign Key in your customer model. You just need to follow Eloquente naming conventions for foreign keys. Look here for more infos.
Eloquent Relations
A custom can have multiple billing addresses. So it customer can have many addresses and an address belongs to a customer.
// Customer Model
public function addresses()
{
return $this->belongsTo('App\BillingAddress', 'customer_id');
}
// BillingAddress Model
public function customer()
{
return $this->hasMany('App\Customer', 'billing_address_id');
}
Then you can do : Customer::with('addresses')->get();
Secondly, make sure you are passing correct foreign key column as second parameter to the relationship methods. See documentation for reference
The second error is probably causing because of incorrect foreign key column passed as second argument.

Access relation of pivot table in Laravel

I have three models Bill, Product and Process. Bill has a ManyToMany relationship with Product and the pivot table have some extra fields. I write the Bill model class like follow:
<?php
class Bill extends Model
{
function products(){
return $this->belongsToMany(\App\Product::class)
->withPivot('process_id') // the id of the Process
->withTimestamps();
}
}
The Process model is a simple table with an id and a name. I am associating the id in the pivot table for reference the Process, the name could change over time but still referring the same concept so I can't associate the name.
The show page for a single Bill lists the products associated in a table like follow:
#foreach($bill->products as $product)
<tr>
<td>{{$product->barcode}}</td>
<td>{{$product->pivot->process_id}}</td>
</tr>
#endforeach
So the problem is that I need the name of the process, but I have the id. I'm not sure how I could get the name.
Thanks
I think you can use an own Pivot Model, e.g. ProductBill in order to achieve this.
class ProductBill extends Pivot {
public function process() {
return $this->belongsTo(Process::class);
}
}
By using this model in your products relation on Bill
class Bill extends Model {
function products() {
return $this->belongsToMany(\App\Product::class)
->withPivot('process_id')
->using(ProductBill::class)
->withTimestamps();
}
}
When accessing $product->pivot you should get an instance of ProductBill now, hence you should be able to do the following:
<td>{{$product->pivot->process->name}}</td>
(Unfortunatelly I not able to doublecheck right now :/)
Without having a direct relation to Process you will likely need to add a helper on your Product model to get the name of Process.
In your Product model:
public function processName($processId) {
return Process::where('id', $processId)->pluck('name')->first();
}
In your view:
<td>{{$product->processName($product->pivot->process_id) }}</td>
There may be a better way, but concept this should work.
I know it's not the most elegant of solutions. but you could always simply do:
Process::find($product->pivot->process_id)->name;
I wouldn't advice this though as you are looping through an array already, so the overheads to doing something like this would be quite large.
Another solution would be to create a Pivot class called say BillProductPivot which would have a relationship to return both Product and Process, then when you call them you should be using eager loading to get the relationships. end product might look like this:
$bill->load('productPivots', 'productPivot.process', 'productPivot.product');
#foreach($bill->productPivots as $productPivot)
<tr>
<td>{{$productPivot->product->barcode}}</td>
<td>{{$productPivot->process->name}}</td>
</tr>
#endforeach

How to get laravel pivot table records in views?

I have three tables.
Users: id,name
Courses: id,user_id,name
Order: id,user_id,course_id (
Pivot table)
How can i make sure in course view that this user has purchased this particular course using Laravel eloquent.
Firstly, you need to define a many to many relationship between your models:
class User extends Model {
public function courses() {
return $this->belongsToMany(Course::class);
}
}
Once you have it, you can easily check if User has bought access to a Course with given ID with:
if ($user->courses()->find($courseId)) {
// user has access to course with given $courseId
}
If you simply want to an error to be raised when course was not bought, replace a call to find() with a call to findOrFail():
if ($user->courses()->findOrFail($courseId)) {
// user has access to course with given $courseId
}

Categories