I have created map table for relation between Products & Product Categories. I want to get Product object from collection using below code.
return $collection->products->product_categories()->get()->filter(function($item) use ($itemIds)
{
if(isset($item->pivot->parent->id))
{
return $item->pivot->parent;
}
});
If I dump and die parent then I can see Product object but when I try to return Product it shows Category data.
You're almost there. You just need to return the product data from the categories. Assuming your code works, you just have to map the collection to return the parent from the pivot.
return $collection->products->product_categories()->get()->filter(function($item) use ($itemIds)
{
if(isset($item->pivot->parent->id))
{
return $item->pivot->parent;
}
})->map(function($item){
return $item->pivot->parent;
});
Related
I'm learning Laravel and need to get a list of nested relationships. My foreign keys seem to be set up correctly.
products->servers->configs
Product Controller
$products = Product::with('servers')->get();
Product Model
public function servers()
{
return $this->hasManyThrough(Config::class, Server::class);
}
I'm only getting a list of servers that are the configs. Eg
products:{
id:1,
servers:[
ram:16gb //this is the config not the server
]
}
How can I get the list of configs inside the servers inside the products? Eg
products:{
id:1,
server:{
id:1,
name:'big server',
config:{
ram:16gb
}
}
}
In Product Modal Use hasMany Method
public function servers()
{
return $this->hasMany(Server::class);
}
In Server Modal Use hasMany(for many rows get) or hasOne(for single row get) method
public function configs()
{
return $this->hasMany(Config::class);
}
public function config()
{
return $this->hasOne(Config::class);
}
Now In ProuctController see how to get nested relationship data
$products = Product::with('servers.configs')->get();
The last few days I've been rocking my head against the wall with the seeders. I can't seem to get the hang of it.
The relationships are very simple:
A Brand hasMany products and each product belongs to a single brand.
A Category hasMany products and each product belongs to a single category.
Given that, I'm creating 5 categories at the beginning so I can retrieve a random one later.
I'm also creating 10 brands, and for each brand I'm creating 50 products and make them belong to that brand. Then I create the relationship with the product and the category, retrieving a random category for each product.
Im getting this error:
PDOException::("SQLSTATE[HY000]: General error: 1364 Field 'brand_id' doesn't have a default value")
I dont understand why I'm getting this error because I'm creating the relationship prior to creating the products:
$brand->products()->saveMany(factory(App\Product::class, 50).
public function run()
{
$users = factory(\App\User::class, 1000)->create();
$categories = factory(\App\Category::class, 5)->create();
factory(App\Brand::class, 10)->create()->each(function ($brand) {
$brand->products()->saveMany(factory(App\Product::class, 50)->make()->each(function ($product) use ($brand) {
$product->category()->associate($this->getRandomCategory());
$product->save();
}));
});
}
private function getRandomCategory() {
return \App\Category::all()->random();
}
private function getRandomUser() {
return \App\User::all()->random();
}
I don't know if I'm making a big deal out of seeding but it just seems complex to me. Maybe I'm taking a wrong approach using the factories. Is there any great tutorial for seeding out there?
Thanks in advance!
This is actually not a seeding problem. The PDO-driver already tells you about the issue:
brand_id does not a have a default value
Probably Laravel assumes, that the id column does not need a default and therefore does not insert it with an id. But the column seems to have no default definition in the database (should be smth. like AUTO_INCREMENT). And that's the reason why you receive an error from the database.
The problem was this one:
The saveMany method gets called after the each function is done, so I was kinda saving the product before it added the brand relationship. Thats why it couldnt assign the foreign_key brand_id. This is the block of code working:
public function run()
{
$users = factory(\App\User::class, 1000)->create();
$categories = factory(\App\Category::class, 5)->create();
factory(App\Brand::class, 10)->create()->each(function ($brand) {
$brand->products()->saveMany(factory(App\Product::class, 50)->make()->each(function ($product) use ($brand) {
$product->category()->associate($this->getRandomCategory());
}));
});
}
private function getRandomCategory() {
return \App\Category::all()->random();
}
private function getRandomUser() {
return \App\User::all()->random();
}
I have the table structures as
products id
packs id
pack_products pack_id, product_id
pack_optional_products pack_id, product_id
So in the above table, pack will have 2 type of products, compulsory products and optional products, mean if customer buy the pack the products which are attached in that pack as compulsory will automatically ordered, but customer also can see if there is any optional product in the pack so customer can also buy that product too.
Now to manage orders I have the following table.
orders id, customer_id
order_pack order_id, pack_id
Here everything works fine, using attach method as $order->packs()->attach($pack["pack_id"]);
So here problem starts ( when I tried to add optional ordered products in the ordered pack), to manage optional products order, I have created the following table
order_pack_product order_id, pack_id, pack_optional_product_id
I have created a model as.
class Order extends Model
{
//
public function packs(){
return $this->belongsToMany(Pack::class)->withTimestamps()->withPivot('name');
}
public function parent() {
return $this->belongsTo(ParentCustomer::class);
}
}
class OrderPack extends Model
{
//
public function optionalProducts()
{
return $this->belongsToMany(PackOptionalProduct::class, 'order_pack_product')->withTimestamps();
}
}
And then I call the method as.
$order->packs()->optionalProducts()->attach($optionalProduct["pack_optional_product_id"]);
and I'm getting this error.
BadMethodCallException: Call to undefined method
Illuminate\Database\Eloquent\Relations\BelongsToMany::optionalProducts()
in file
/.../....../laravel/framework/src/Illuminate/Support/Traits/ForwardsCalls.php
on line 50
SO complete code related to above tables will be this.
$order = new Order();
$order->parent_id = $parent->id;
$order->school_id = $schoolId;
if($order->save()){
foreach($json["packs"] as $pack){
$order->packs()->attach($pack["pack_id"], ["child_name" => $pack["child_name"]]);
foreach($pack["optional_products"] as $optionalProduct){
$order->packs()->optionalProducts()->attach($optionalProduct["pack_optional_product_id"]);
}
}
return response()->json(["status" => "ok", "order_id" => $order->id]);
}else{
return response()->json(["status" => "failed"]);
}
optionalProducts() is method of model, not of relation packs().
Method $order->packs() - returns relation object, attribute $order->packs - returns collection of pack models.
You need to iterate trough collection, to attach optionalProducts to every one pack model.
foreach($pack["optional_products"] as $optionalProduct){
foreach($order->packs as $item){
$item->optionalProducts()->attach($optionalProduct["pack_optional_product_id"])
}
}
Perhaps, this code will not correct the error, then you need to correct your code logic.
I have some working queries that are not ideal and I'd like to try perform them the Eloquent way to tidy it up.
I think I've got the relationships defined correctly but I can't figure out how to write the queries and store them in variables in the controller. I need to return these variables back to the view because I json encode them for use in JavaScript.
Here's what I have in my controller:
public function show($idProject)
{
$project = ProjectsModel::with('user')->where('idProjects','=',$idProject)->first();
$objsettings = ObjSettingsModel::where('idObjSettings','=',$project['ObjSettingsID1'])->first();
$obj = ObjModel::where('idObjs','=',$objsettings['Objs_idObjs'])->first();
return view('project')->with('project',$project)->with('obj',$obj)->with('objsettings',$objsettings);
}
The naming conventions are a bit off so here's what this does.
I pass the $idProject to the controller from a link on my index page where I've looped through and paginated all rows from the Projects table.
The first query finds the project row where it's id (idProjects) matches the variable passed from the index page via the link (idProject). I've also successfully pulled the related user row from the user table using an eloquent relationship.
The next query pulls from an ObjSettings table which stores a number of settings values for an object which is shown on the page. I match the idObjSettings column of the ObjSettings table to the previously pulled $project['ObjSettingsID1'] which is essentially the foreign key in the projects table. There can be several ObjSettings for each Project.
The 3rd query pulls from the Obj table which stores details about an object. These are static details on objects such as name or size for example. I match the idObjs column to the previously pulled $objsettings['Objs_idObjs'] which is the foreign key in the ObjSettings table. One Obj can have many ObjSettings which are used in other Projects.
Here's how I'm passing the php variables to JS:
<script>var obj = #json($obj);</script>
<script>var objsettings = #json($objsettings);</script>
Here are my relationships
ProjectsModel
public function user()
{
return $this->belongsTo('App\User', 'Username', 'id');
}
public function objsettings()
{
return $this->hasMany('App\ObjSettingsModel', 'idObjSettings', 'ObjSettingsID1' );
}
ObjSettingsModel
public function objs()
{
return $this->belongsTo('App\ObjsModel', 'Objs_idObjs', 'idObjs');
}
public function projects()
{
return $this->belongsTo('App\ProjectsModel', 'ObjSettingsID1', 'idObjSettings' );
}
ObjModel
public function objsettings()
{
return $this->hasMany('App\ObjSettingsModel', 'idObjs', 'Objs_idObjs');
}
I've tried a whole range of queries such as:
$project = ProjectsModel::with('user')->with('objsettings.objs')->where('idProjects','=',$idProject)->first();
$objsettings = $project->objsettings;
$obj = $project->objsettings->objs;
but I keep running into issues such as "Property [objs] does not exist on this collection instance." I suppose I'm returning multiple rows in this case? Any help would be appreciated.
You need to loop through objsettings
$project = ProjectsModel::with('user')->with('objsettings.objs')->where('idProjects','=',$idProject)->first();
$objsettings = $project->objsettings;
foreach($objsettings as $objsetting){
$objs = $objsetting->objs;
}
I'm not sure this is a real relation. I will try to explain the best way I can.
So first of all, I have three models :
Appartement,
AppartementPrice
The AppartementPrice depends on :
- appartement_id
I would like the AppartementPrice to be retrieve like that :
If there is a specific price for the appartement, then retrieve it, If not retrieve the price for all appartement which is stored in the database with an appartement_id = 0.
So basically what I would like is to do something like that :
public function price()
{
if(isset($this->hasOne('AppartementPrice')->price) // Check that relation exists
return $this->hasOne('AppartementPrice');
else
return $this->hasOne('AppartementPrice')->where('appartement_id', '0');
}
But this is not working.
It does not retrive me the default price.
I guess anyway this is not a best practice ?
I first tried to get the informations like that :
//Check if appartment has a specific price or retrieve default
if($priceAppartement = AppartementPrice::getPriceByCompanyAppartement($this->id))
return $priceAppartement;
else
return AppartementPrice::getDefaultPrice();
But I had this error :
Relationship method must return an object of type Illuminate\Database\Eloquent\Relations\Relation
when doing :
echo $app->price->price;
How can I check that a relation exists ? And is there a way to do as I describe ?
Thank you
You can't replace relation like this, as what you intend is not logical - you want to retrieve relation that doesn't exist.
Instead you can do this:
public function getPriceAttribute()
{
return ($this->priceRelation) ?: $this->priceDefault();
}
public function priceDefault()
{
// edit: let's cache this one so you don't call the query everytime
// you want the price
return AppartmentPrice::remember(5)->find(0);
}
public function priceRelation()
{
return $this->hasOne('AppartementPrice');
}
Then you achieve what you wanted:
$app->price; // returns AppartmentPrice object related or default one
HOWEVER mind that you won't be able to work on the relation like normally:
$price = new AppartmentPrice([...]);
$app->price()->save($price); // will not work, instead use:
$app->priceRelation()->save($price);
First of all something really important in Laravel 4.
When you do not use parentheses when querying relationship it means you want to retreive a Collention of your Model.
You have to use parentheses if you want to continue your query.
Ex:
// for getting prices collection (if not hasOne). (look like AppartementPrice)
$appartment->price;
// for getting the query which will ask the DB to get all
//price attached to this appartment, and then you can continue querying
$priceQuery = $appartment->price();
// Or you can chain your query
$appartment->price()->where('price', '>', 0)->get() // or first() or count();
Secondly, your question.
//Appartement Model
// This function is needed to keep querying the DB
public function price()
{
return $this->hasOne('AppartementPrice')
}
// This one is for getting the appartment price, like you want to
public function getAppartmentPrice()
{
$price_object = $this->price;
if (!$price_object) // Appartment does not have any price {
return AppartementPrice->where('appartement_id', '=', 0)->get();
}
return $price_object;
}