I am setting up several Models an want to know the correct approach to table structure and Model relationships.
Let's assume we have a shop containing products, each with properties size and color.
Table products
id
size_id
color_id
price
Table sizes
id
name
Table colors
id
name
Models
class Product extends Eloquent {
public function size() {
return $this->hasOne('Size', 'id');
}
public function color() {
return $this->hasOne('Color', 'id');
}
}
class Size extends Eloquent {
public function products() {
return $this->belongsTo('Product', 'size_id');
}
}
class Color extends Eloquent {
public function products() {
return $this->belongsTo('Product', 'color_id');
}
}
This way I can easily echo the color/size of a product using {{ Product->size['name'] }}. Also, I want to pass Eloquent the size's foreign key size.id like Product::where('size_id', '5') rather than its name size.name.
Problem: Doing $products = Product::has('size', '=', '5')->get() does not give me any results, yet doing $products = Product::where('size_id', '5')->get() does.
I am pretty confused, what went wrong?
I think that the problem is that your ::has() method is looking for products with exactly 5 different sizes on each specific product, which would assume that you would be using $this->hasMany('Size') in your Product model. Where as the ::where() method is returning results where the size of the product is 5.
In the documentation they use an example of comments. A post will have a list of comments. You can find posts that have at least one comment (ie. Post::has('comments')->get()) or you can find posts that have more than 3 comments (ie. Post::has('comments', '>=', '3')->get()).
http://laravel.com/docs/eloquent#querying-relations
Related
there are two tables products and categories, that I created by PHPMyAdmin.
In the products table, it has a column name prd_category that has the foreign key of table categories named cat_id(primary key of categories table).
i am quite new in laravel
i want return all data from product table with category name(cat_name) from another table
//here is my controller
use App\Models\product;
class items extends Controller
{
public function sample(){
return product::all();
}
}
//route
Route::get('/',[items::class,'sample']);
//model for products table
class product extends Model
{
use HasFactory;
function category(){
return $this->hasOne('App\Models\category','cat_id','prd_id');
}
}
//model for category
class category extends Model
{
protected $table='categories';
use HasFactory;
}
pls help and thanks in advance..
you can use this code:
$products = product::whereHas('category',function($q)use($cat_name){
$q->where('name',$cat_name)
})->get();
or :
$categories = Category::where('name',$cat_name)->get()->pluck('id')->toArray()
$products = product::whereIn('category_id',$categories)->get();
Are you sure that one-to-many relation is correct? If a product can belong to many categories, you need to use many-to-many relations. Furthermore, if something else belongs to categories you should use many-to-many polymorphic relations. But let's go with one-to-many.
First, the relation function in Product.php looks incorrect. I think products should belong to a category.
function category(){
return $this->belongsTo('App\Models\Category','cust_name','name');
}
Then you need to define reverse relation in Category.php
function products(){
return $this->hasMany('App\Models\Product','cust_name','name');
}
When you define the relations properly, all you need yo do is to fetch data in controller:
use App\Models\Category
...
Category::with('products')->get();
you can use relationship with forign key like pro_id
function category(){
return $this->belongsTo('App\Models\Category','pro_id');
}
function products(){
return $this->hasMany('App\Models\Product','id');
}
$cat = Category::with('products')->all();
in Blade :
{{ $cat->products->cat_name; }}
I feel like this should work. I have a list of products and categories (types).
Tables:
Products
- id
- name
- etc
Types
- id
- name
- etc
ProductTypes
- product_id
- type_id
Now, I feel like in the Type model in Laravel, I should be able to define this relationship:
public function products()
{
return $this->hasManyThrough(Product::class, ProductType::class, 'type_id', 'id');
}
I've tried other variations with the secondary ids in the additional parameters but no luck, always an empty list. Is ProductTypes a pivot table and therefore should be dealt with differently?
Edit: What's weird is that for the final 2 parameters ($localKey = null, $secondLocalKey = null) even if I enter complete garbage no error is thrown but these 2 parameters $firstKey = null, $secondKey = null have to be correct).
You are using the wrong relationship. Based on your database structure, a product can belong to many type. Therefore, it should be a BelongsToMany instead of a HasManyThrough.
You can achieve what you want with the following method, by passing the table name of your ProductTypes as the second parameter:
public function products()
{
return $this->belongsToMany(Product::class, 'product_types');
}
If your ProductType model extends Illuminate\Database\Eloquent\Relations\Pivot, you can do:
public function products()
{
return $this->belongsToMany(Product::class, 'product_types')
->using(ProductType::class);
}
For more information about Many to Many relationships: https://laravel.com/docs/6.x/eloquent-relationships#many-to-many
I have the following relationship in the database, each table with its respective and related model.
I have products where each product has its recipe, which is composed of zero or more elements and the amount needed.
After this I have a warehouse table, where I have the current stock of the element, what I want is to obtain the products in which the amount needed in the recipe is less than the stock.
How could I obtain this data? it occurred to me to create a scope in the model products but I do not know how to perform the validation since they can be 0 or n elements in the recipe ?
Controller
Product::with('recipe')
->enableElements()->get();
Model Product
public function scopeEnableElements($query)
{
}
You can query from Recipe to WareHouse by relating the element_id.
Recipe model:
public function warehouses()
{
return $this->hasMany(\App\Models\WareHouse::class, 'element_id', 'element_id');
}
WareHouse model:
public function recipes()
{
return $this->hasMany(\App\Models\Recipe::class, 'element_id', 'element_id');
}
Controller code:
Product::whereHas('recipes', function($q) {
$q->whereHas('warehouses', function($query) {
$query->whereRaw('WareHouse.stock > Recipe.count'); // use table names here, not model names
});
})->get();
If you wanted to do it with a scope in the Recipe model:
public function scopeEnableElements($query)
{
$query->whereHas('warehouses', function($query2) {
$query2->whereRaw('WareHouse.stock > Recipe.count'); // use table names here, not model names
});
}
Then in your controller:
Product::whereHas('recipes', function($query) {
$query->enableElements();
})->get();
I have products and prices table (Temporal Data).
what is the best approach for getting the latest price of a specific product?
here's the basic structure of my two tables:
products table:
-ID
-name
prices table
-ID
-product_id
-amount
-effective_datetime
Product Model:
public function prices()
{
return $this->hasMany('App\Price', 'product_id', 'id');
}
Price Model:
public function product()
{
return $this->belongsTo('App\Product', 'product_id');
}
Im currently using this code to get the latest price of a product:
$product->prices->sortByDesc('effective_datetime')->first()->amount
As you can imagine, I have to call that long line all over my application just to get the latest price of a product. is there a better way?
My idea is to create a queryScope on my Price model like this:
public function scopeLatest($query)
{
return $query->sortBy('effective_datetime', 'desc')->first();
}
and Call
$product->prices->latest()->amount
but laravel is throwing an error "Call to undefined method Illuminate\Database\Eloquent\Collection::latest()"
In order to apply scopes to a relationship, you have to call them on the relationship's method (rather than its dynamic property) to allow query chaining. Try this:
$product->prices()->latest()->amount
(Originally commented)
In your case it would be ideal to have an accessor:
public function getPrice()
{
$this->prices()->sortBy('effective_datetime', 'desc')->first()->amount;
}
Now you can use it like $product->price.
We work on a database where table names are named locally.
table.product
-- id
-- picture
-- category_id
table.category
-- id
-- caterory_name
-- category_directory
There are some products in table. product.category_id should point category.id so system will understand that product belongs to desired table.
We could do this by using INNER JOIN but we can't do it with Laravel. We probably need to configure our models with has/belongs_to etc.
This is where we struggle.
//Controller
$products = Product::all();
return View::make('theme-admin.product_list')
->with('products', $products);
//View (in foreach)
{{ URL::to('uploads/categories/[NEED_CATEGORY.CATEGORY_DIRECTORY_HERE]/' . $products[$k]->picture) }}
We can't get Category.category_directory info in our views since we pass Product::all() only.
How can we make it so the $products array also contains category.category_directory for each value and we can access it like; $products[$k]->category_name?
Thanks!
Create a category relation in your Product model:
class Product extends Eloquent {
private $table = 'myProductsTableName';
public function category()
{
return $this->belongsTo('Category', 'myCategoryColumnID');
}
}
class Category extends Eloquent {
private $table = 'myCategoriesTableName';
}
Unless you need to select all products from a particular category, you don't need to create a hasMany relation on your Category model.
And you just need to use myCategoryColumnID if your products table doesn't follow the singular_table_name_id rule (product_id).
Then just use it:
//View (in foreach)
{{ URL::to('uploads/categories/'.$products[$k]->category->category_directory.'/' . $products[$k]->picture) }}
I would setup a relationship...
class Product extends Eloquent {
public function category()
{
return $this->belongsTo('Category');
}
}
class Category extends Eloquent {
public function product()
{
return $this->hasMany('Product');
}
}
The name you use in the realtionship is the Model name.....so be sure if you're using a different table name than your model name, that you set that in your model
protected $table = "yourtablename";
Then use it like so...
{{ URL::to('uploads/categories/'.$products[$k]->category->category_directory.'/'
You still end up querying the database multiple times this way... This is called the N+1 effect. Example, if you have 5 products, one query will be executed to get those products. Then in the loop we are executing a query to get the category. This results in 6 total queries.
To solve this problem, use eager loading, which reduces those 6 queries in our example down to 2.
$products = Product::with('category')->get();
Then send that to your view, where you can do your foreach loop..
foreach($products as $val){
//To output the directory
echo $val->category->category_directory;
}
Or in blade...
#foreach($products as $val)
{{URL::to('uploads/categories/'.$val->category->category_directory.'/'.$val->picture)}}
#endfor