Laravel - How to pluck field from nested relations - php

I'm trying to pluck field from nested relations.
My class structure is:
User hasMany GroupUser
GroupUser belongsTo Group
Group belongsToMany Promotion
So this way I can get fine get Promotions from an User
$user->groupUsers()->with('group')->with('group.promotions')->get();
{
"id": 4,
....
"promotions": [
{
"id": 3,
"group_id": 11,
"user_id": 4,
...
"group": {
"id": 11,
...
"promotions": [
{
"id": 8,
"title": "Lavagem Mensal 1 unid.",
"const": "LAVAGEM_MENSAL1_UNID",
"pivot": {
"group_id": 11,
"promotion_id": 8
}
}
]
}
}
]
}
So what I need is to get a pluck list of Promotions.const.
Tried this way but it returns [null]
$user->groupUsers()->with('group')->with('group.promotions')->get()->pluck('group.promotions.const');
UPDATE: Now this way I close to achieve it.
$user->groupUsers()->with('group')->with('group.promotions')->get()->pluck('group.promotions.*.const');
"promotions": [
[
"BRF_LAVAGEM_MENSAL_1_UNID"
]
]
Just need to flat this array.

Try this:
$user->groupUsers()
->with('group.promotions')
->get()
->pluck('group.promotions.*.const')
->collapse();

Related

Laravel Eloquent how to get relationship self object?

I have tables with below relationship
And my HousingAdvertisement model has
public function nearPlaces()
{
return $this->hasMany(HousingAdNearPlace::class);
}
and HousingAdNearPlace
public function nearPlace()
{
return $this->hasOne(NearPlace::class, 'id');
}
when I make query like this:
HousingAdvertisement::with('nearPlaces.nearPlace')->where('user_id', '=', auth()->user()->id)->get();
I got HousingAdNearPlace object in HousingAdvertisement model:
[...
{
...,
"near_places": [
{
"id": 27,
"housing_advertisement_id": 48,
"near_place_id": 3,
"created_at": "2021-06-29T12:23:35.000000Z",
"updated_at": "2021-06-29T12:23:35.000000Z",
"near_place": null
},
{
"id": 28,
"housing_advertisement_id": 48,
"near_place_id": 4,
"created_at": "2021-06-29T12:23:35.000000Z",
"updated_at": "2021-06-29T12:23:35.000000Z",
"near_place": null
}
]
...]
How can I got self NearPlace model like this:
[...
{
...,
"near_places": [
{
"id": 3,
"name": "Park",
"slug": "park",
"created_at": "2021-06-29T06:25:57.000000Z",
"updated_at": "2021-06-29T06:25:57.000000Z"
},
{
"id": 4,
"name": "Beach",
"slug": "beach",
"created_at": "2021-06-29T06:25:57.000000Z",
"updated_at": "2021-06-29T06:25:57.000000Z"
}
]
...]
You need "Has Many Through" relationship on HousingAdvertisement
public function nearPlaces()
{
return $this->hasManyThrough(NearPlace::class, HousingAdNearPlace::class);
}
And also define id keys as in ducumentation: https://laravel.com/docs/8.x/eloquent-relationships#has-many-through-key-conventions
You need to use laravel Model Accessor for generating slug from HousingAdvertisement's name row.
For that use -
public function slug()
{
return Str::lower($this->name);
}
This will make a slug from name attribute. Add it on your HousingAdvertisement model. Now you need to cast it with query builder.
For that use -
protected $casts = [
'slug' => 'string',
];
Add this on your HousingAdvertisement model. Finally you can query your database like -
HousingAdvertisement::find(auth()->user()->id)->get(['id', 'name', 'slug', 'created_at', 'updated_at']);

Laravel get multiple tables

I'm trying to build something like product variants, so far so good. But there is one problem.
I'm getting product and variables as separate json
Example:
[
{
"id": 1,
"panelgameid": 71,
"name": "Counter-Strike 1.6",
"active": 1,
"minslots": 12,
"maxslots": 32,
"slotincreament": 2,
"order": 1,
"gameid": 1,
"location": "Serbia",
"price": 0.6
},
{
"id": 2,
"panelgameid": 71,
"name": "Counter-Strike 1.6",
"active": 1,
"minslots": 12,
"maxslots": 32,
"slotincreament": 2,
"order": 1,
"gameid": 1,
"location": "Germany",
"price": 0.4
}
]
And I have my games and my prices. But I want to show them like this.
[
{
"id": 1,
"panelgameid": 71,
"name": "Counter-Strike 1.6",
"active": 1,
"minslots": 12,
"maxslots": 32,
"slotincreament": 2,
"order": 1,
"gameid": 1,
"prices": [
{
"location": "USA",
"price": 0.6
},
"location": "Germany",
"price": 0.4
]
}
]
This is my current code as CONTROLLER:
$games = DB::table('games')
->join('prices', 'games.id', '=', 'prices.gameid')
->get();
return $games;
Create the Game and Price models by make:model artisan command:
php artisan make:model Game
php artisan make:model Price
You will find them at your project /app folder. There define the relationships.
Game Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Game extends Model
{
// define the relationship with the Price model, one game has many prices
public function prices()
{
return $this->hasMany(Price::class, 'gameid'); // you will have to make explicit the foreign key. Looking at the query in your question, I assume that it's gameid
}
}
Price Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Price extends Model
{
// define the relationship to the Game model, one price belongs to a Game
public function game()
{
return $this->belongsTo(Game::class, 'gameid');
}
}
Then you can query the games with the prices in your Controller:
$games = Game::with('prices')->get();
You can read more about Eloquent ORM in the docs: Eloquent: Getting Started, and Eloquent: One To Many Relationships.

Laravel joining table with relation model return as json

I have three tables:
products table (id, name)
ingredients table (id, ingredient_name)
products_ingredients table (product_id, ingredient_id)
I have a relation defined in Product
public function ingredients(){
return $this->hasMany('App\ProductIngredient','product_id','id');
}
When I am listing products using
$products = Product::where('category_id',$cat->id)->with('ingredients')->get();
I am getting this output
"products": [{
"id": 1,
"name": "Hesp",
"category_id": 1,
"ingredients": [{
"id": 41,
"product_id": 1,
"ingredient_id": 4,
},
{
"id": 42,
"product_id": 1,
"ingredient_id": 5,
}]
}]
Which is right.
What I want is to add the ingredient name in ingredients list like this
"products": [{
"id": 1,
"name": "Hesp",
"category_id": 1,
"ingredients": [{
"id": 41,
"product_id": 1,
"ingredient_id": 4,
"ingredient_name": "some name" // I want to add this field
},
{
"id": 42,
"product_id": 1,
"ingredient_id": 5,
"ingredient_name": "some name" // I want to add this field
}]
}]
I want to return this as JSON to use in API not in blade where I can call other relations.
How can I achieve this?
You have implemented the many-to-many relationship all wrong. Per the documentation:
Many-to-many relationships are defined by writing a method that returns the result of the belongsToMany method.
...
To define the inverse of a many-to-many relationship, you place another call to belongsToMany on your related model.
class Product extends Model
{
public function ingredients()
{
return $this->belongsToMany('App\Ingredient', 'products_ingredients');
}
}
class Ingredient extends Model
{
public function products()
{
return $this->belongsToMany('App\Product', 'products_ingredients');
}
}
Note the related model is first argument to belongsToMany(). It's not needed to create a model for a simple pivot table. Because you have misnamed the pivot table, it must be specified as the second argument to the belongsToMany(). The default name is singular model in alphabetic order, i.e. ingredient_product.
Your output should look something like this now, including the values from the ingredients table, not the pivot table.
"products": [{
"id": 1,
"name": "Hesp",
"category_id": 1,
"ingredients": [{
"id": 4,
"ingredient_name": "some name"
},
{
"id": 5,
"ingredient_name": "some name"
}]
}]

Whats the best approach to create filter-based search in Laravel or raw SQL?

I'm working on a query to find offers based in different filters and need some guidance on how to do that.
My model structure is basically: offer -> place -> categories -> filters -> filterOptions
offer (belongsTo) <-> (hasMany) place
place (belongsToMany) <-> (belongsToMany) category
place (belongsToMany) <-> (belongsToMany) filter
filter (belongsToMany) <-> (belongsToMany) category
filter (hasMany) <-> (belongsToMany) filterOption
Search params comes in request JSON just like this below, which says "get offers from places with category 1 and has those filters + filterOptions"
{
"category_id":1,
"filters":[
{
"filter_id":1,
"options":[
{"option_id":2},
{"option_id":3}
]
},
{
"filter_id":2,
"options":[
{"option_id":4},
{"option_id":6}
]
}
]
}
So far I'm just getting the entire list without filtering.
$q = Offer::with([
'place',
'place.categories',
'place.categories.filters',
'place.categories.filters.filterOptions',
])
This is returning the JSON objects like this:
[
{
"id": 4,
"place_id": 2,
"place": {
"id": 2,
"categories": [
{
"id": 3,
"name": "Mechanics",
"pivot": {
"place_id": 2,
"category_id": 3
},
"filters": [
{
"id": 5,
"name": "Services",
"pivot": {
"category_id": 3,
"filter_id": 5,
},
"filter_options": [
{
"id": 8,
"name": "Tires",
"filter_id": 5
},
{
"id": 9,
"name": "Painting",
"filter_id": 5
}
]
},
{
"id": 6,
"name": "Amenities",
"pivot": {
"category_id": 3,
"filter_id": 6,
},
"filter_options": [
{
"id": 11,
"name": "Wifi",
"filter_id": 6
},
{
"id": 12,
"name": "Food",
"filter_id": 6
}
]
}
]
}
]
}
}
]
I have no idea on what is the starting point to perform this filtering.
Should I use eloquent or raw query? single or multiple queries? joins or grouping?
Solved this way:
$params = json_decode($request->getContent());
$q = Offer::with([
'place:id,title',
'place.categories:id,name',
'place.categories.filters:id,name',
'place.categories.filters.filterOptions:id,name,filter_id'
])
->whereHas('place.categories', function($q) use ($params){
$q->where('id', $params->category_id);
})
->whereHas('place.categories.filters', function($q) use ($params){
$filters = array_flatten(array_pluck($params->filters, 'filter_id'));
$q->whereIn('id', $filters);
})
->whereHas('place.categories.filters.filterOptions', function($q) use ($params){
$filterOptions = array_flatten(array_pluck($params->filters, 'options.*.option_id'));
$q->whereIn('id', $filterOptions);
})
->select('id','percent','place_id')
->get();

Laravel loop based on inner values

I have a competition model (the competition is essentially a large form to fill in), that hasMany groups (groups of fields), which hasMany fields. When a user fills in a form, I create one entry record which hasMany field submissions (called elements). So, one entry might look like the following:
id form_id
-------------
2 5
The the field submissions (elements) table may look like the following:
id entry_id field_id value
----------------------------------
1 2 2 Dave
2 2 3 30
So if I run:
$entry = Entry::with('elements.field.group')->find($id);
I get something like this:
{
"id": 16,
"competition_id": 1,
"elements": [
{
"id": 8,
"field_id": 1,
"entry_id": 16,
"content": "F",
"field": {
"id": 1,
"name": "gender",
"label": "Gender",
"type": "radio",
"group_id": 1,
"group": {
"id": 1,
"order": 1,
"competition_id": 1
}
}
},
{
"id": 9,
"field_id": 2,
"entry_id": 16,
"content": "fds",
"field": {
"id": 2,
"name": "name",
"label": "Your name",
"type": "text",
"group_id": 1,
"group": {
"id": 1,
"order": 1,
"competition_id": 1
}
}
}
]
}
To display these, I'd like to ideally do a #foreach($groups as $group), but the groups only exist in element->field->group. How can I go about reorganising the array to allow for this? Is there a way to rearrange the array from the inside out? Or is there a way of sorting the data as I grab it from Laravel?
Maybe this works:
$groups = Group::with('fields.element.entry')->whereHas('field.element.entry', function($q) use ($id){
$q->where('entries.id', $id);
})->get();

Categories