Getting data from two table with one query - json

I have two tables posts and images and I want to get all the posts and related images with below code:
$posts = Post::leftJoin('images','posts.postId','images.postId')
->where('countryId', $id)
->groupBy('posts.postId')
->orderby('posts.postId', 'desc')
->select('posts.*','images.*', 'posts.postId as postId')
->get();
But the problem is that I get only one image of the post instead of all images because one post have multiple images.
Current Json response is:
[
{
"postId": 103,
"text": 'This is the post text',
"countryId": 75,
"imageId": 152,
"imageLink": "C9hGzwVKlOrL.jpg",
"imageDescription": ""
},
...
]
And I want to change it like this:
[
{
"postId": 103,
"text": 'This is the post text',
"countryId": 75,
"images" : [
{
"imageId": 152,
"imageLink": "C9hGzwVKlOrL.jpg",
"imageDescription": "This is the 1st image description"
},
{
"imageId": 153,
"imageLink": "JHKJdiuIuoi.jpg",
"imageDescription": "This is the 2nd image description"
},
...
]
},
...
]

You shouldn't try to get hierarchical/nested data using one query. Eloquent has "Relationships" functionality for that: https://laravel.com/docs/8.x/eloquent-relationships#one-to-many
In your case you need to add "images" relation to Post model:
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
public function images()
{
return $this->hasMany(Image::class, 'postId', 'postId');
}
}
Then:
$posts = Post::where('countryId', $id)
->orderBy('postId', 'desc')
->select('posts.*','images.*', 'posts.postId as postId')
->get();
foreach ($posts as $post) {
$post->images; // = collection of all images related to this post
}

Related

How to create object from arrays of row in laravel Elequent

Here I want to make the array inside "property" to an object where the key of the object will be the value of 'key_name' column of product_property table
[{
"sort_order": 1,
"name": "Samosas",
"visible": 1,
"description": null,
"property": [
{
"product_uuid": "95be9cf4-7121-492b-8725-762e6353ac51",
"key_name": "categories",
"key_value": "Starter"
},
{
"product_uuid": "95be9cf4-7121-492b-8725-762e6353ac51",
"key_name": "print_order",
"key_value": "1"
}
]
}]
This is what I want
[{
"sort_order": 1,
"name": "Samosas",
"visible": 1,
"description": null,
"property": {
"categories": "Starter",
"print_order": "1"
}
}]
I tried with Product::with('property')->get() and it results the first array.
Is there any Eloquent way to do that or raw sql or Query Builder?
Looping throw the result and make that object is possible but here i want something from SQL end
$products = Product::with('property')->get();
foreach ($products as $product) {
$array = [];
foreach ($product->property as $property) {
$array[$property['key_name']] = $property['key_value'];
}
$product->properties = $array;
}
This looping giving me the results but its from php end
public function property(){
return $this->hasMany(ProductProperty::class, 'product_uuid', 'uuid');
}
The Product model
Product table
"sort_order",
"name",
"visible",
"description",
Product Property table
"product_uuid",
"key_name",
"key_value"
Thanks in advance
$products = Product::with('property')->get();
foreach ($products as $product) {
$array = [];
foreach ($product->property as $property) {
$array[$property['key_name']] = $property['key_value'];
}
$product->properties = (object) $array;
}
Add object. It will convert this to a std object.

Laravel: Resources with json field

I am on Laravel 7.x and I have two models (CustomerOrder composed of many CustomerOrderLines) with parent - child relationship. Parent (CustomerOrder) model has a json type field among its fields.
CustomerOrderResource.php:
return [
'id' => $this->id,
'wfx_oc_no' => $this->wfx_oc_no,
'qty_json' => json_decode($this->qty_json)
];
CustomerOrderLineResource.php:
return [
'id' => $this->id,
'description' => $this->description,
'customer-order' => $this->customerOrder
];
CustomerOrder->GET request returns properly formatted data as:
"data": {
"id": 11,
"wfx_oc_no": 12,
"qty_json": {
"L": "20",
"M": "30",
"S": "20",
"XL": "100"
}
}
But for CustomerOrderLine->GET, the response is as:
"data": {
"id": 15,
"description": "test desc",
"customer-order": {
"id": 11,
"wfx_oc_no": 12,
"qty_json": "{\"L\": \"20\", \"M\": \"30\", \"S\": \"20\", \"XL\": \"100\"}"
}
}
json field is not properly formatted. It seems it doesn't go through Resource class. Please let me know, how can I get this fixed?
FYI
CustomerOrderLine.php:
public function parent()
{
return $this->belongsTo(CustomerOrder::class);
}
Finally managed to get it solved using json cast. The field was included in $casts array in the model.

Laravel group by multiple level

How to GROUP BY multiple levels in Laravel. I've tried this code.
I've also used toArray() then array_values(), but same result as using values()
EDIT: Sorry, I wrote value() instead of values()
When using values(), product_name not showing.
// Controller
$products = DB::table('product_registrations')
// ->select(['product_name', 'color'])
->join('colors', 'colors.id', '=', 'product_registrations.color_id')
->join('sizes', 'sizes.id', '=', 'product_registrations.size_id')
->join('products', 'products.id', '=', 'product_registrations.product_id')
->get()
->groupBy(['product_name', 'color']);
// ->values(); // I've used values(), it worked but first level groupBy name missing
return response()->json(['products' => $products]);
---
I want something like:
products: [
product1: [
color1: {},
color2: {}
]
]
Without values()
{
"products": {
"light blue dress": {
"Ягаан": [
{
"id": 1,
"product_id": 1,
"color_id": 1,
"size_id": 6,
"color": "Ягаан",
"product_name": "light blue dress",
}
],
},
}
}
With values()
{
"products": [
{
"Ягаан": [
{
"id": 1,
"product_id": 1,
"color_id": 1,
"size_id": 6,
"color": "Ягаан",
"product_name": "light blue dress",
}
],
}
]
}
Try this one. I also have problem on this last week but I manage to get through.
$products::latest()->get();
$products = products->groupBy(function($product) {
return $product->name;
});
$products = products->map(function($product) {
return $product->groupBy(function($prod) {
return $prod->color;
});
});
return $products;
You can also optimize this method by using array_only this method is only for laravel

Laravel using avg() function with foreign key

I want to get the average rates for each product.
I have a rates table which has a foreign key to product table,
the rates table is similar to this
when I try to get products with this code:
$stocks = Stocks::with('images:url,color', 'tags:tag', 'sizes', 'rates')
->get()
->pluckDistant('tags', 'tag')
->pluckDistant('sizes', 'size');
it returns this
[
{
"id": 10,
"name": "name",
"image": "1564964985mI7jTuQEZxD49SGTce6Qntl7U8QDnc8uhVxedyYN.jpeg",
"images": [
{
"url": "1564964985mI7jTuQEZxD49SGTce6Qntl7U8QDnc8uhVxedyYN.jpeg",
"color": ""
},
{
"url": "1564964985EV20c1jGvCVCzpCv2Gy9r5TnWM0hMpCBsiRbe8pI.png",
"color": ""
},
{
"url": "1564964985iFcMox6rjsUaM8CHil5oQ9HkrsDqTrqLNY1cXCRX.png",
"color": ""
}
],
"tags": [
"عطور"
],
"sizes": [],
"rates": [
{
"id": 1,
"stocks_id": 10,
"rate": 2
},
{
"id": 2,
"stocks_id": 10,
"rate": 4
}
],
}
]
How can I get the average of rates as "rates":3 using the eloquent relations to get them all by sql without php proccessing?
You could leverage something like Appending. Say you have a Product model which has a OneToMany relationship with Rate model.
Your Product model would look something like this:
class Product extends Model
{
protected $with = ['rates'];
protected $appends = ['average_rate'];
public function getAverageRateAttribute()
{
return $this->attributes['average_rate'] = $this->rates->avg('rate');
}
public function rates() {
return $this->hasMany(Rate::class);
}
}
Now anytime you query your products from the database, you'll have the rate appended with the result.
array:7 [▼
"id" => 1
"created_at" => "2019-08-12 14:08:09"
"updated_at" => "2019-08-12 14:08:09"
"average_rate" => 4.5
"rates" => array:2 [▶]
]
However, be aware of causing n+1 problem. If you're using this approach make sure to always eager load your rates.
You could just use join and use aggregate function on rates table.
Stocks::with('images:url,color', 'tags:tag', 'sizes')
->join('rates', 'rates.stocks_id', '=', 'stocks.id')
->selectRaw('stocks.*')
->selectRaw('AVG(rates.rate) as average_rating')
->selectRaw('tags.*')
->selectRaw('sizes.*')
->selectRaw('images.*')
->groupBy('stocks.id')
->get()

Laravel Array & JSON Casting to Algolia

I am trying to send some data along to Algolia through the toSearchableArray. Any strings I have stored in my DB are sending along fine, but I hit a roadblock when trying to push nested JSON data along—the information is being sent as a string with characters escaped.
This is a sample of the nested object that I am storing in my table (MySQL with a JSON data type):
[
{
"id": 19,
"name": "Mathematics",
"short": "Math"
},
{
"id": 23,
"name": "Science",
"short": "Science"
},
{
"id": 14,
"name": "Health and Life Skills",
"short": "Health"
}
]
My model looks like this:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;
class Resource extends Model
{
use Searchable;
protected $primaryKey = 'objectID';
public function toSearchableArray()
{
$data = $this->toArray();
$data['grades'] = explode(';', $data['grades']);
$data['units'] = explode(';', $data['units']);
return $data;
}
}
I get an output that looks like this:
array:22 [
"objectID" => 1
"name" => "Resource #1"
"slug" => "resource-1"
"write_up" => """
This is an example write up.
"""
"author" => "johnny"
"type_name" => "Lesson Plan"
"language" => "English"
"grades" => array:3 [
0 => "Kindergarten"
1 => "Grade 1"
2 => "Grade 4"
]
"subjects" => "[{"id": 19, "name": "Mathematics", "short": "Math"}, {"id": 23, "name": "Science", "short": "Science"}, {"id": 14, "name": "Health and Life Skills", "short": "Health"}]"
"units" => array:2 [
0 => "Unit A"
1 => "Unit B"
]
"main_image" => "https://dummyimage.com/250x325/000000/fff.png&text=Just+a+Test"
"loves" => 88
"downloads" => 280
"created_at" => "2018-01-01 13:26:47"
"updated_at" => "2018-01-02 10:10:32"
]
As you can see, the 'subjects' attribute is being stored as a string. I know there is attribute casting in 5.5 (I am running 5.5), but I am not too clear on how I would implement the example they have for Array & JSON Casting in my work above. https://laravel.com/docs/5.5/eloquent-mutators#attribute-casting
Would anyone be willing to show me an example?
I'd rely on Attribute Casting for this, add a $casts property in your model and it will be done automatically.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;
class Resource extends Model
{
use Searchable;
protected $primaryKey = 'objectID';
protected $casts = [
'subjects' => 'array',
];
public function toSearchableArray()
{
// Same function as you posted
}
}
You can also do it manually in your toSearchableArray method with $data['subjects'] = json_decode($this->subjects, true);
I answered with more details on this other posts: https://discourse.algolia.com/t/laravel-array-json-casting-to-algolia/4125/2