How to get data from two related tables in laravel 5 - php

I have 3 tables with foreign keys installed.
customers {customer_id, customer_name}
products {product_id, product_name}
customer_products {id, customer_id (foreignkey), product_id (foreignkey)}
My Controller code:
$CustomerProducts = ModelName::where('customer_id', 'somevalue')
->Join('customer_products', 'product_id', '=', 'customer_id')
->get();
My Model code:
class ModelName extends Model {
protected $table = 'hd_products';
public $primaryKey = 'id'; }
What is wrong in my code, since I'm getting wrong results. I want to show customer information and its related products.

This is where Laravel makes life easy. By adding the relationships on the model, you can then simply call the relationship via an eager load. You don't need the join, you can just pull the relationship. So
On your Customer model, set up the product relationship (you look like you have the right database structure for a many-to-many):
public function products(){
return $this->belongsToMany("\App\Product");
}
And then in your Controller, when you go to load your customers, you can grab the products at the same time:
$customer = Customer::with("products")->first();
I'm just taking the first customer as an example - you could get all of them and loop on customer as well as products if you wish.
And finally when you want to call the data like in a blade view, you can access it by chaining on the $customer model. :
{{ $customer->products->first()->name }}
If you want to loop through the products on the customer in a blade view:
#foreach($customer->products as $product){}
And, you still have the primary data for the $customer:
$customer->name // Etc.
HTH

If you want to show customer information and its related products, you gotta select the data from the tables.
In your code, in the controller, to get all data from all tables
you add:
->select(['customers.*' ,'products.*' ,'customer_products.*'])->get();
and edit the join statement so the controller will be like:
$CustomerProducts= DB::table('customer_products')
->join('customers','customers.customer_id','customer_products.customer_id')
->join('products','products.product_id','customer_products.product_id')
->select(['customers.*' ,'products.*' ,'customer_products.*'])
->get();
do not forget to add (if not added)
use DB;
At the beginning of your file (in the namespace area or the imports area), so it is like:
namespace App\Http\Controllers;
use DB;
use App\ //"your_file";
use Illuminate\Http\Request;
Hope this is helpful :)

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.

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

ID lost despite left join

I am using a left join to combine two tables based on a common column but I seem to lose the primary key id in my left table. Every other column is returned
My controller:
$prod_manu = $request->prod_manu;
return $data = Product::leftJoin('manufacturers','products.Manufacturer_id','=','manufacturers.id')
->where('products.Manufacturer_id',$prod_manu)
->get();
Product Model:
class Product extends Model{
public function types(){
return $this->belongsTo('App\types','Type_id');
}
public function manufacturers(){
return $this->belongsTo('App\manufacturers','Manufacturer_id');
}
}
This is probably because both Product and manufacturers have an id column, so one of them gets overwritten.
Instead of using the left join, you can use eloquent's with method.
If you have your relationships and class structure setup properly you should be able to access the Manufacturer's products like this:
$manufacturer = Manufacturer::where('id', $prod_manu)->with('products')->get();
return $manufacturer->products;
Also note that class names should always be singular and start with a capital letter in laravel. App\manufacturers Shouldn't be valid. Please read the article I linked above about setting up your eloquent relationships.

Laravel eloquent multiple table join with filter

There are theree tables in my system.
Students
Articles
categories
Student can write many articles and a article is belong to just one student. And A Article can have only one category.
Controller
public function all_articles_by_student_by_category(Request $request){
$students_id = $request->students_id;
$categories_id = $request->categories_id;
$article_list = Students::find($students_id)->articles->all();
//This return Something like, Select All Articles Written by Damith
}
Model
class Students extends Model
{
protected $fillable = ['id','first_name', 'last_name', 'age', 'created_at', 'updated_at'];
public function articles()
{
return $this->hasMany('App\Articles');
}
}
What I am try to get
Something like, Select All Articles Written by Damith for Technology Category (Category Name should be there)
What I able to do so far
Something like, Select All Articles Written by Damith using $article_list = Students::find($students_id)->articles->all(); (You can find this code from controller)
What I want from you
How do I modify $article_list = Students::find($students_id)->articles->all(); to get, something like, Select All Articles Written by Damith for Technology Category. (Category name must be there in result and it is on category table, and for where condtion you can use the category_id which is i the article table )
First off with what you have done so far the ->all() method is not needed when getting the records for a relation on a model, this would return all of the articles linked to that student:
Students::find($students_id)->articles
Go through Articles Model
You could do something like:
Article::where('student_id', $students_id)
->where('category_id', $category_id)->get();
Which would acheive the result you are after.
Go through Students Model
If you want to go through Students Model you can constrain the relation using the with method.
$student = Students::with(['articles' => function($query) use ($category_id) {
$query->where('category_id', $category_id);
}])->find($student_id);
$filteredArticles = $student->articles
Useful Links
Laravel Docs 5.5 for Eager Loading : https://laravel.com/docs/5.5/eloquent-relationships#eager-loading
When accessing Eloquent relationships as properties, the relationship data is "lazy loaded". This means the relationship data is not actually loaded until you first access the property. However, Eloquent can "eager load" relationships at the time you query the parent model.
Laravel Docs 5.5 for Constraining Eager Loads: https://laravel.com/docs/5.5/eloquent-relationships#constraining-eager-loads
Sometimes you may wish to eager load a relationship, but also specify additional query constraints for the eager loading query.
Something like this should work:
$technologyArticles = Articles::where('student_id', '=', $students_id)->where('category_id', '=', $categories_id)->get();

In Laravel (5.3), how do I perform a loop through two separate DB tables and send the results into a view?

I am VERY new to Laravel, so please bear with me if this is a stupid question.
Here is ultimately what I am trying to do.
I have two database tables: Categories and FoodItems. Let's say for simplicities sake, that I have two categories in the Categories table (Appetizers, and Sandwiches), and I have 3 items in the FoodItems table:
Onion Rings, which has a foreign key pointing to the Appetizers table
Mozzarella Sticks, which has a foreign key pointing to the Appetizers table
Hamburger, which has a foreign key pointing to the Sandwiches table
I have a view, where I want to first display all of the categories, and then show all of the items in that category.
So my view would display:
Appetizers:
- Onion Rings
- Mozzarella Sticks
Sandwiches:
- Hamburger
I can't figure out how I could loop through these two tables, do a nested DB call, and then send this structure into my view, without doing any of the logic actually inside the view.
Could anybody please tell me what the best practice here would be, to get the data, as I have it ordered, into my view?
Edits/Extra Info:
Just to let everybody know where I am at (I am still struggling). Here is all of my code.
First, I have two database tables set up. The first is called "foodItems" and the second is called "foodCategories".
The foodItems table has the following columns:
id, uniqueID, categoryID, clientID, itemName, itemDescription, itemAlias, thumbURL, itemSize, itemCost, itemPrice, isTaxable, hasSizes, hasDescriptors, status, created_at, updated_at
The foodCategories table has the following columns:
id, uniqueID, clientID, categoryName, categoryAlias, status, created_at, updated_at
Next, I have two models, FoodItem, and FoodCategory.
The code I have in my FoodItem model looks like so:
namespace App;
use Illuminate\Database\Eloquent\Model;
class FoodItem extends Model {
protected $table = 'foodItems';
public function foodCategory(){
return $this->belongsTo('App\FoodCategory');
}
}
The code in my FoodCategory model looks like so:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class FoodCategory extends Model {
protected $table = 'foodCategories';
public function foodItem() {
return $this->hasMany('App\FoodItem');
}
}
The code in my controller looks like this:
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Http\Request;
use App\FoodCategory;
use App\FoodItem;
class DashboardController extends Controller {
public function __construct(){
$this->middleware('auth');
}
public function food() {
$food_items = FoodItem::orderBy('categoryID')->get();
return view('food', compact('food_items'));
}
}
And finally, the code I have in my View (food.blade.php) is:
#foreach ($food_items as $food)
<li>{{ $food->foodCategory->categoryName }}</li>
#endforeach
In my view, I have tried using parenthesis after "foodCategory", I have tried changing the name to "FoodCategory", "FoodCategory()", "foodCategories()", "foodCategories"... and about every other combination I can think of - but nothing seems to work.
As the code is right now, it produces the following error:
"Trying to get property of non-object (View: /Applications/XAMPP/xamppfiles/htdocs/Laravel/ChanceSystems2017/resources/views/food.blade.php)"
I'm really stumped at this point. Could anybody please shed some light onto what I am missing? Thank you!
Your problem can be solved by using relationships. Follow this link for more information
This video helped me a lot in understanding relationships in laravel. Hope it helps you too.

Categories