Laravel relation has many with comma seprated - php

I want to get a relation, here is my code:
OrderController.php
public function orders()
{
$orders = Order::with('packages')->OrderBy('created_at', 'DESC')->paginate();
return view('admin/orders/orders')->with(compact('orders'));
}
Order.php
function packages(){
return $this->hasMany('App\Models\Package', 'id','cart_items');
}
but the problem is cart_items contains a value like this (5,6) comma separated, how can I get relation hasMany from array?

Its's not possible by ORM need to work with mutator and accessor. Please remove packages method and try this in your Order model;
protected $appends = ['packages'];
public function getPackagesAttribute ()
{
if ($this->cart_items) {
$cart_items = explode(',', $this->cart_items);
if(count($cart_items)>0) {
return Package::whereIn('id', $cart_items)->get();
}
} return null;
}

Related

Retrieving data from hasMany Relationship

I want to show data from 'personas' (parent table) that has at least one 'residente' (child table), its a one to many relationship, and i want to show data of that residente too.
I was trying to do it using the has() method like the laravel documentation says:
https://laravel.com/docs/9.x/eloquent-relationships#querying-relationship-existence
but it does not work.
Models looks like this
//in the Persona class
public function residentes()
{
return $this->hasMany(Residente::class);
}
//in the Residente class
public function persona()
{
return $this->belongsTo(Persona::class);
}
//in the PersonasController
public function index()
{
$personas = Persona::has('residentes')->get();
dd($personas);
}
the Result
enter image description here
//it doesn't get the data from "residentes"
Try :
public function index()
{
$personas = Persona::with('residentes')->get();
dd($personas);
}
If you want to search using some keys inside the residentes relationship you can use whereHas
Persona::with('residentes')
->whereHas('residentes',function($residente){
return $residente->where('column_name',$value);
})->get();
Also try to mention the local_key and the foreign_key in the relationship itself reference : https://laravel.com/docs/9.x/eloquent-relationships
return $this->hasMany(Comment::class, 'foreign_key', 'local_key');
Please try the following in the place of key give actual field names.
//in the Persona class
public function residentes()
{
return $this->hasMany(Residente::class, 'foreign_key', 'local_key');
}
//in the Residente class
public function persona()
{
return $this->belongsTo(Persona::class,'foreign_key', 'owner_key');
}
//in the PersonasController
public function index()
{
$personas = Persona::with('residentes')->get();
foreach($personas as $person){
echo "<pre>";
print_r($person->residentes()->get()->toArray());
}
}

How to get a custom collection based on a data that is most repeated (from top to bottom))

I have a table like this:
Basically this table is named favourite_products and contains product ids that are added as favourite for users.
Now I wanted to get a collection of most added product from this table.
So in this case, a product with an id of 10 would be on top of the collection.
But I don't know how to get this collection ordering by from most repeated product id (prd_id)...
Here is the Model:
class FavouriteProduct extends Model
{
protected $table = 'favourite_products';
protected $fillable = ['usr_id','prd_id'];
public function user()
{
return $this->belongsTo(User::class, 'usr_id');
}
public function product()
{
return $this->belongsTo(Product::class, 'prd_id');
}
}
UPDATE #1:
Product.php Model:
public function favouritees()
{
return $this->belongsToMany(User::class, 'favourite_products', 'prd_id', 'usr_id');
}
I think the following code solve your problem:
$most_liked_products = DB::table('favourite_products')
->select(DB::raw('count(prd_id) as total'), id)
->groupBy('total')
->orderByDesc('total')
->get();
Please try it and give your feedback
try use this
public function example()
{
$data=FavouriteProduct::orderBy('prd_id', 'ASC')->get();
dd($data);
}

Laravel: unable to retrieve one to many camelCase method

I am trying to retrieve the inverse side of the one to many relationship where the method is camelCase. i.e
Owning Class: OneToMany
class Brand extends Model
{
public function products()
{
return $this->hasMany(Product::class);
}
}
Owned Class
class Product extends Model
{
public function MyBrand()
{
return $this->belongsTo(Brand::class);
}
}
retrieve the inverse related model like this:
$product = Product::find(1);
$brand = $product->my_brand;
dd($brand->name);
Error, Trying to get property 'name' of non-object
I also tried this:
$brand = $product->myBrand;
it did not work.
however, if i make my method like below it works:
public function brand()
{
return $this->belongsTo(Brand::class);
}
question is : how to make it work when the method is in CameCase ?
Try this,
public function my_brand()
{
return $this->belongsTo(Brand::class);
}
And call this relation as,
$product = Product::find(1);
$brand = $product->my_brand;
dd($brand->name);
You need to change, method name in brand(), because Eloquent will automatically determine the proper foreign key column in the Brands table.
https://laravel.com/docs/7.x/eloquent-relationships#one-to-many
If you want to keep it MyBrand() you need to specify foreign key:
public function MyBrand()
{
return $this->belongsTo(Brand::class,'product_id');
}
Specify it's foreign key in relationship
class Product extends Model
{
public function MyBrand()
{
return $this->belongsTo(Brand::class,'brand_id');
//brand_id is foreign key in your product table
}
}
$product = Product::find(1)->with('MyBrand'); //But it will be good to eager load it
$product->MyBrand->name; //It will definitely return name now

Laravel ORM dynamic model attributes

I have a database set up that I am not sure how to code in laravel. I am trying to basically get dynamic attribute names from custom input.
Here's the DB setup:
Category:
-ID
-Name
Product:
-ID
-Category_id
Product_Attribute:
-ID
-Category_id
-Attribute_Name
Attribute_value:
-ID
-Product_id
-Product_attribute_id
-Value
There can be multiple values for each attribute and I don't have a set list of attributes as it can change depending on the category/product. Some products have some attributes and some don't. Some will have the same key/name as in other categories but will rarely overlap for my purposes but i can work around that if need be.
I there a way to setup laravel so i can look through the keys / values as well as call them by name
echo $product->$Attribute_Name;
or
echo $product->attributes[$Attribute_Name];
or something similar
but i also need to pull all products where attribute name = y and attribute Value = X
select * from Products join Attribute_value on products.ID = Attribute_value.Product_id join Product_Attribute on Category_id = Products.Category_id and Product_Attribute.ID = Attribute_value.Product_attribute_id where Product_Attribute = '{attribute_name}' and Attribute_value = '{Attribute_value}'
This is only return the products but not with the associated data or the other attributes. I can't find and easy way of loading that data without having to build a class to populate it. Ideally I would like to be able to change the values and save them using the ORM similar to how a one to many relationship works.
I have seen this type of structure before in databases. I was wondering if there was a way to do this easily in laravel without having to create a bunch of custom functions to load the attributes for each product.
Thanks
I'm a little confused by your question, but what you first want to do is create all the models and relationships. You don't necessarily need a model for each of the four tables, but I'd strongly recommend it.
class Category
{
public function products(): HasMany
{
return $this->hasMany(Product::class);
}
public function productAttributes(): HasMany
{
return $this->hasMany(ProductAttribute::class);
}
}
class Product
{
public function category(): BelongsTo
{
return $this->belongsTo(Category::class);
}
public function attributeValues(): HasMany
{
return $this->hasMany(AttributeValue::class);
}
}
class ProductAttribute
{
public function category(): BelongsTo
{
return $this->belongsTo(Category::class);
}
public function attributeValues(): HasMany
{
return $this->hasMany(AttributeValue::class);
}
}
class AttributeValue
{
public function product(): BelongsTo
{
return $this->belongsTo(Product::class);
}
public function productAttribute(): BelongsTo
{
return $this->belongsTo(ProductAttribute::class);
}
}
This code expects you to consider the Laravel naming standards of tables and properties.
After defining the classes and relationships, you may load products with their attributes like this:
$products = Product::query()
->with('attributeValues.productAttribute')
->where('category_id', $categoryId)
->get();
Because this makes accessing an attribute by it's name a pain...
$product = $products->first();
$color = optional($product->attributeValues
->where('productAttribute.name', 'color')
->first())->value ?? 'white';
... you can also override the __get($name) method to add a nice accessor for your attributes:
class Product
{
public function __get(string $name)
{
if ($name === 'attrs') {
return (object) $this->attributeValues->mapWithKeys(function ($attributeValue) {
return [$attributeValue->productAttribute->name => $attributeValue->value];
});
}
return parent::__get($name);
}
}
After doing so, you should be able to access your attributes like this:
$product = $products->first();
$color = $product->attrs->color;
// or if you need to retrieve an attribute by name stored in a variable
$name = 'color';
$attr = $product->attrs->$name;
Of course you can also omit the (object) cast in the __get($name) accessor to return an array instead. You then receive this syntax: $product->attrs['color']. Either way, this will return an error if a property is not set / not in the array. Make sure to catch this. You may also want to add some caching to avoid building the attrs object/array over and over again.
Please note: The $attributes property is used by the Eloquent base model internally to store all the properties of a model. So this name is reserved and you should use something else like attrs instead.
Edit: Two more options for getters would be the following ones:
class Product
{
public function getColorAttribute(string $default = 'white'): string
{
return optional($this->attributeValues
->where('productAttribute.name', 'color')
->first())->value ?? $default;
}
public function getAttr(string $name, $default = null)
{
return optional($this->attributeValues
->where('productAttribute.name', $name)
->first())->value ?? $default;
}
}
Similarly, you could design a setter:
class Product
{
public function setAttr(string $name, $value): void
{
if ($this->hasAttr($name)) {
$attr = $this->attributeValues
->where('productAttribute.name', $name)
->first();
$attr->value = $value;
$attr->save();
} else {
throw new \Exception(sprintf('This product may not have the attribute [%s].', $name));
}
}
public function hasAttr(string $name): bool
{
return $this->attributeValues
->contains('productAttribute.name', $name);
}
}

Fetch data nested relations eloquent with() in Laravel

I have a model file "Student" that contains the relation :
public function implementations()
{
return $this->hasMany(Implementation::class);
}
In my Implementation model, I have theses relations :
public function score()
{
return $this->belongsTo(Score::class, 'id', 'implementation_id');
}
public function project()
{
return $this->belongsTo(Project::class);
}
I would like to retrieve a table with all its data.
I tried this
public function getStudents($id)
{
$event = Event::where('id', $id)->first();
$data = $event->students()->with('implementations')->get();
return response()->json($data);
}
It works. But I do not have the result I would like. I would also like to recover the implementations data with theproject and score relationships
I tried that but it does not work
public function getStudents($id)
{
$event = Event::where('id', $id)->first();
$data = $event->students()->with('implementations')->with('project', 'score')->get();
return response()->json($data);
}
Thank you very much for your help!
Have you tried Event::with('students', 'students.implementations') yet? You can eager load relations of relations using the dot notation.

Categories