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
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
]);
}
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();
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.
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),
];
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;
}, []));
}