How to group attribute values? - laravel

How to get this result:
foreach ($products as $product) {
$product->name
foreach ($product->attributes as $attribute) {
$attribute->name
foreach ($attribute->values as $value) {
$value->name
}
}
}
And
{
"name": "Product",
"attributes": [
{
"name": "Attribute 1",
"values": [
"name": "Value 1",
"name": "Value 2",
]
}
]
}
Instead of this:
foreach ($products as $product) {
$product->name
foreach ($product->attributeValues as $attributeValue) {
$attributeValue->attribute->name
$attributeValue->name
}
}
And
{
"name": "Product",
"attribute_values": [
{
"attribute_name": "Attribute 1",
"value_name": "Value 1",
},
{
"attribute_name": "Attribute 1",
"value_name": "Value 2",
}
]
}
Database
products
--- id
--- name
attributes
--- id
--- name
attribute_values
--- id
--- attribute_id
--- name
attribute_value_product
--- id
--- attribute_value_id
--- product_id
Products
Many-to-Many relationship for Attribute Values.
Attributes
One-to-Many relationship for Attribute Values.
Attribute Values
One-to-Many (Belongs To) relationship for Attributes.
Many-to-Many relationship for Products.
I'm struggling to find solution but failed so i posted here for get some solution.
Please help me.

Your desired output
"values": [
"name": "Value 1",
"name": "Value 2",
]
that is not a valid json, you cannot have an array with the same key, you either remove the key or wrap each value in array to have a key name.
Anyway, you can just define the relationship in your product to pull the Attribute Values and have a relationship in your attribute value for the attribute name,
something like this,
Produc Model
// get the attribute_values assign to products
public function attributeValues() {
return $this->belongsToMany(AttributeValue::class);
}
Attribute Value Model
// get the attribute name assign to attribute value
public function attrName() {
return $this->belongsTo(Attribute::class, 'attribute_id');
}
You can then query the related relationship and modify the attribute_values to suit your required format.
sample;
assume you have this attribute;
[{ id: 1, name: "weight" }, { id: 2, name: "style" }, { id: 3, name: "color" } ]
Create your product
Product::create([
'name' => 'Product 2'
])->attributeValues()->createMany([
['attribute_id' => 1, 'name' => '1.3kg'],
['attribute_id' => 2, 'name' => 'something nice'],
['attribute_id' => 3, 'name' => 'yellow'],
['attribute_id' => 3, 'name' => 'black'],
['attribute_id' => 3, 'name' => 'orange'],
]);
Then your product query
// eager load attribute values and attribute values name relationship
$products = Product::select('id','name')
->with(['attributeValues', 'attributeValues.attrName' ])
->paginate();
// modify and format the attribute values according to your needs before returning the products
// refer to Laravel Collection docs on how you can modify a collection and its data
$products->map( function($item) {
$item->attributes = collect($item->attributeValues)
->groupBy('attrName.id')
->values()
->map(fn ($group, $key) => [
'name' => data_get($group, '0.attrName.name'),
'values' => $group->pluck('name')->all()
])
->values();
//Remove the original attributeValues data
unset($item['attributeValues']);
return $item;
});
// Return the paginated products
return $products;
you should get a product data format like this
{
id: 2,
name: "Product 2",
attributes: [
{
name: "weight",
values: [
"1.3kg"
]
},
{
name: "style",
values: [
"something nice"
]
},
{
name: "color",
values: [
"yellow",
"black",
"orange"
]
}
]
}
Additionally, you can just append a pre-formmated custom attribute values in your Product model which will automatically loads everytime you query a product.
e.i.
Product Model
protected $appends = ['attributes'];
public function attributeValues() {
return $this->belongsToMany(AttributeValue::class);
}
protected function getAttributesAttribute() {
return $this->attributeValues()->get()
->groupBy('attrName.id')
->values()
->map(fn ($group, $key) => [
'name' => data_get($group, '0.attrName.name'),
'values' => $group->pluck('name')->all()
])
->values();
}
then you can just use
return Product::select('id','name')->paginate();
or
return Product::find(1);
and the attribute attributes will be automatically in the response
the code above uses collection methods groupBy, map, values.
Check the Collection docs for more info

Related

How to store item with one-to-many relations into sql-table?

I have any form created on vuetify.js
In this for I have field:
Normal input field "Name", will this be stored in the table "person" (model Person, structure: id, name, ...).
Multiple select "Languages", should this be stored in the table "persons_languages" (structure: id, person_id, language_id).
1 person can speak more than 1-2-3 languages.
<v-form ref="form" #submit.prevent="addPerson">
<v-row>
<v-col
cols="12"
md="6">
<v-text-field
v-model="person.name"
label="Name">
</v-text-field>
</v-col>
<v-col
cols="12"
md="6">
<v-autocomplete
chips
deletable-chips
multiple
v-model="???"
:items="allLanguages"
:item-text="item =>`${item.name} (${item.local_name})`"
item-value="id"
label="Spoken languages">
</v-autocomplete>
</v-col>
</v-row>
</v-form>
<script>
export default {
data() {
return {
person: {},
allLanguages: [
{
"id": 1,
"name": "Afar",
"code": "aa"
},
{
"id": 2,
"name": "Abkhazian",
"code": "ab"
},
{
"id": 3,
"name": "Afrikaans",
"code": "af"
}
]
}
},
methods: {
addPerson() {
this.axios
.post('/api/persons', this.person)
.then(response => {
this.$router.push({name: 'indexPersons'});
})
.catch(error => console.log(error))
},
},
}
</script>
How to store array of selected languages into "persons_languages" with ID of new not yet created person?
Tnx.
One of the way to save one to many relation data is using foreach loop. let you create a person which have 2 languages.
$person= Person::create([
'name' => 'SOmthing',
]);
Person select 02 languages. $languauges variable have multiple data like:
$languages=[
{
"id": 1,
"name": "Afar",
"code": "aa"
},
{
"id": 3,
"name": "Afrikaans",
"code": "af"
}
]
Use foreach loop to save languages data in person_languages table against person Id create above.
foreach($languages as $lang) {
PersonLanguage::create([
'person_id' => $person->id,
'language_id' => $lang->id
]);
}
You may use the create method, which accepts an array of attributes, creates a model, and inserts it into the database. The save/create method will automatically add the appropriate person_id value to the new Personlanguages model.
Read Documentation to more about Save Many and Create Many Save/ Create Many
For example one post have many comments.
$post = App\Post::find(1);
$post->comments()->createMany([
[
'message' => 'A new comment.',
],
[
'message' => 'Another new comment.',
],
]);
thanks a lot!
I've created this code:
foreach ($request->addresses as $key => $value) {
//echo "langId = {$value}";
PersonsAddresses::create([
'person_id' => $person->id,
'address_id' => $value
]);
}

How can I add pagination in Lumen 8 collection?

I am using Lumen to create api where I integrate collection. All code details are given below. I want to add pagination according to my code. How I add paginator in my code?
In Controller:
//To get all Employee Type
public function getAllEmployeeTypes(){
$employeeType = OsEmployeeType::where('status',1)->orderBy('priority', 'DESC')->get();
return new OsEmployeeTypeCollection($employeeType);
}
In Collection it looks like
public function toArray($request)
{
return [
'data' => $this->collection->map(function($data) {
return [
'id' => $data->id,
'name' => $data->name,
'priority' => $data->priority,
'status' => $data->status,
];
})
];
}
public function with($request)
{
return [
'success' => true,
'status' => 200
];
}
and the response that I got
{
"data": [
{
"id": 3,
"name": "Type 3",
"priority": 3,
"status": 1
},
{
"id": 2,
"name": "Type 2",
"priority": 2,
"status": 1
},
{
"id": 1,
"name": "Type 1",
"priority": 1,
"status": 1
}
],
"success": true,
"status": 200
}
How can I paginate my API response?
Paginate is the same as Laravel in Lumen. You will do just the same as in Laravel.
$employeeType = OsEmployeeType::where('status',1)->orderBy('priority', 'DESC')->skip(10)->take(5)->get();

Laravel: How to access Object inside an array

I trying to return access the attributes inside a array of object and it is giving this error Exception: Property [id] does not exist on this collection instance.
Here is what I have tried:
protected function formatted($classroom)
{
return [
'courses' => [
'id' => $classroom->courses->id,
'name' => $classroom->courses->name,
'slug' => $classroom->courses->slug,
'coursteachers' => [
'id' => '$classroom->courses->coursteachers->id',
'email' => '$classroom->courses->coursteachers->email',
'uid' => '$classroom->courses->coursteachers->uid',
]
],
];
}
And here is the actual data:
"courses": [
{
"id": 1,
"name": "Analytics",
"slug": "analytics",
"status_id": 1,
"deleted_at": null,
"pivot": {
"classroom_id": 2,
"courses_id": 1
},
"coursteachers": [
{
"id": 3,
"uid": "S0120-46890",
"email": "teacher#vschool.com",
"user_type": "Teacher",
}]
}]
You need to iterate through courses and also courseteachers since they both represent an array but rather an object
protected function formatted($classroom)
{
$result = [];
foreach($classroom->courses as $course) {
$result[] = [
'courses' => [
'id' => $classroom->courses->id,
'name' => $classroom->courses->name,
'slug' => $classroom->courses->slug,
'coursteachers' => $this->getCourseTeachers($cours)
],
];
}
return $result;
}
private function getClassroomTeachers($classroom) {
$result = [];
foreach($classroom->courses as $cours)
{
foreach ($cours->coursteachers as $key => $teacher) {
$result[] = [
// 'coursteachers'=> [
'id' => $teacher->id,
'email' => $teacher->email,
'uid' => $teacher->uid,
'last_name' => $teacher->profile->last_name,
'first_name' => $teacher->profile->first_name,
'middle_name' => $teacher->profile->middle_name,
// ],
];
}
}
return $result;
}
Since courses is an array, you should pick an object using the proper index.
Foe example: try $classroom->courses[0]->id instead of $classroom->courses->id. Here, '0' is the index.
Also it is better if you check if the index exists before you do it. For an example.
'id' : isset($classroom->courses[$index]) ? $classroom->courses[$index]->id : ''
Edit:
In case of a Eloquent collection you should use $classroom->courses->get($index)->id instead of array like retrieval.

Laravel API Resource Collection return specific fields from other resource

I am working on an api that will be accessible via a mobile app.
I have defined resources and collections for respective endpoints. My problem is now is that I want to return different api json data based on what ever collection.
Here is an example
Provinces has cities and suburbs, so in json format I need to have
"data": [
{
"id": 1,
"name": "Eastern Cape",
"cities": [
{
"name": "Alice"
},
],
"suburbs": [
"name": "Suburb 1"
]
},
]
I want different data when the cities resource is called in news api collection
"data": [
{
"id": 1,
"name": "Eastern Cape",
"cities": [
{
"name": "Alice",
"municipality": "municipality name",
},
],
"suburbs": [
"name": "Suburb 1",
"ward_number": "ward 1"
]
},
]
This is an NewsResource Api
public function toArray($request)
{
// return parent::toArray($request);
return [
'id'=> $this->id,
'title' => $this->title,
'slug' => $this->slug,
'content' => $this->content,
'created_at' => $this->created_at,
'category_id' => $this->news_category_id,
'featured_image' => url($this->featured_image),
'author' => new UserResource($this->user),
'category' => new NewsCategoryResource($this->category), //Only category Name to be displayed
'municipality' => new MunicipalityResource($this->municipality), // Only Municipality Name to be displayed
'comments' => new NewsCommentResource($this->comments),
];
}
I don't really know what is your code structure, but hope this help for you
You may use different queries, for example
/** For the obvious resource calling */
return SomeResource::collection (SomeModel::with(['suburbs', 'cities'])
/** Other filter queries */
->get());
/** For the cities resource calling */
return SomeResource::collection (SomeModel::with(['suburbs:id,ward_number', 'cities:id,municipality'])
/** Other filter queries */
->get());
In your Resource class which you use for the cities/suburbs data, do it like so
return [
/** Some data which you need */
'municipality' => $this->when($this->municipality !== null, $this->municipality),
'ward_number' => $this->when($this->ward_number !== null, $this->ward_number),
];

Merge results of query

I have a query that outputs its values as json in the following format:
[
{
"name":"Bob",
"date":"2016-02-05 00:00:00",
"value":34
},
{
"name":"John",
"date":"2016-02-05 00:00:00",
"value":5
},
{
"name":"Bob",
"date":"2016-02-05 00:00:00",
"value":3
},
{
"name":"Sarah",
"date":"2016-02-05 00:00:00",
"value":56
}
...
]
I need to put this data into the form:
[
{
"name":"Bob",
"data": [
[2016-02-05 00:00:00, 34],
[2016-02-05 00:00:00, 3]
]
},
{
"name":"John",
"data": [
[2016-02-05 00:00:00, 5]
]
},
{
"name":"Sarah",
"data": [
[2016-02-05 00:00:00, 56]
]
}
...
]
Or in other words, I need to combine the results that share a name and put the data into an array data, where each array holds the date and value.
The original json data is held in a variable $results:
foreach ($results as $result)
{
//
}
How do I achieve this?
Something like
function process_results($results) {
$out = [];
foreach ($results as $result) {
$name = $result['name'];
if ( ! $out[$name]) {
$out[$result['name']] = array( 'name' => $name, 'data' => array());
}
array_push($out[$name]['data'], array($result['date'], $result['value']));
}
return array_values($out);
}
This also seems to work
function process_results($results) {
return array_values(array_reduce($results, function($acc, $result) {
$name = $result['name'];
if ( ! $acc[$name]) {
$acc[$result['name']] = array( 'name' => $name, 'data' => array());
}
$acc[$name]['data'][] = array($result['date'], $result['value']);
return $acc;
}, []));
}

Resources