I have a relational database whose models look like this:
Restaurant:
class Restaurant extends Authenticatable
{
public function dishes()
{
// return $this->hasMany(Dish::class);
return $this->belongsToMany('App\Models\Dish');
}
}
Dish:
class Dish extends Model
{
public function orders()
{
return $this->belongsToMany('App\Models\Order')->withPivot('quantity');
}
public function restaurant()
{
return $this->belongsToMany('App\Models\Restaurant');
}
}
Order
class Order extends Model
{
public function dishes()
{
return $this->belongsToMany('App\Models\Dish');
}
}
I'm retrieving all orders belonging to the restaurant by id like so:
public function index($id)
{
$user_id = $id;
$dishes = Dish::where('restaurant_id', $user_id)->get();
foreach ($dishes as $dish => $value) {
$orders = $value->orders;
foreach ($orders as $order => $value) {
$response[] = $value;
}
}
return response()->json($response);
}
This works, but I would like to group these results by order_id in some way, so that I get returned something similar to this:
{
"id": 28,
"status": 1,
"address": "Fish Street",
"user_name": "Artyom",
"user_surname": "Pyotrovich",
"phone": "351 351 643 52",
"email": "artyom#restaurant.com",
"total": 35.8,
"created_at": "2021-11-17T10:44:58.000000Z",
"updated_at": "2021-11-17T10:44:58.000000Z",
"dishes": [{
"dish_id": 22,
"quantity": 3
},
{
"dish_id": 23,
"quantity": 1
}]
}
Is this possible by simply changing the Eloquent query or do I need to perform several operations on the result of my current query? Also interested in what is actually the best practice in these cases.
Thanks in advance
You can use the ::with() method to get all the desired relations, this is much cleaner then a foreach rewrite.
Example:
$orders = Order::with('dishes')
->whereHas('dishes', function(Builder $dishes) use ($user_id) {
$dishes->where('restaurant_id', $user_id);
})->get();
return response()->json( $order );
Small note:
Separate the user id from the restaurant id, what if the user has multiple restaurants?
Related
I'm trying to figure out how to exclude/hide the relationship column in the eager load collection result array
I have 2 models and here are the relations
Product.php
class Product extends Model
{
public function Category()
{
return $this->belongsTo(Category::class);
}
}
Category.php
class Category extends Model
{
public function Product()
{
return $this->hasMany(Product::class);
}
}
Table fields
Category: id, name
Product: id, name, category_id
so, here is my query
$result = Product::leftJoin('product_tags', 'product.id', 'product_tags.product_id')
->with(['category' => function ($q) {
$q->selectRaw('id,name as category_name');
}])
->select('product.id', 'product.name','product.category_id',DB::raw('group_concat(DISTINCT(product_tags.product_tag)) as product_tag'))
->groupBy('product_tags.product_id')
->paginate($limit)
->toArray();
here is the response
{
"id": 50,
"name": "three",
"category_id": 2, // this field I need to exclude from result array
"product_tag": "123,3",
"category": {
"id": 2,
"category_name": "Azo"
}
}
below is the response I'm excepting
{
"id": 50,
"name": "three",
"product_tag": "123,3",
"category": {
"id": 2,
"category_name": "Azo"
}
}
I've tried doing it like so:
1.
$result['data'] = collect($result['data'])->except(['category_id']);
$result['data'] = collect($result)->transform(function($i) {
unset($i->category_id);
return $i;
});
even I tried to use the except() helper function but seems all the efforts are meaningless
NOTE: I'm aware I can set protected properties in the model ($hidden or $visible), I might want to use it in different contexts and want to use the default pagination of laravel.
Is it possible and any way of doing this?
Many thanks.
On the first try, every child will have category_id, not the primary array.
collect($result)->map(function($item) {
return collect($item)->except('category_id');
})->toArray();
On the second one, you're using toArray() in your main array so to unset category_id you need to use the []
unset($i['category_id']);
PS: I see paginate() it's the result of this the result of an endpoint? In that case, you can check out API Resource.
You can use makeHidden() method.
$paginator = Product::leftJoin('product_tags', 'product.id', 'product_tags.product_id')
->with(['category' => function ($q) {
$q->selectRaw('id,name as category_name');
}])
->select('product.id', 'product.name','product.category_id',DB::raw('group_concat(DISTINCT(product_tags.product_tag)) as product_tag'))
->groupBy('product_tags.product_id')
->paginate($limit);
$result = $paginator->makeHidden(['product.category_id'])->toArray();
I have 3 tables (foods, orders and orderItems). I want to get orders of a user with orderItems and foods, but i failed.
Here's a result what i am getting right now, but i need to get food details instead of food_id.
[
{
"id": 16,
"user_id": 2,
"total": "12.50",
"items": [
{
"id": 20,
"order_id": 16,
"food_id": 1,
"quantity": 1,
"food_price": "12.50",
}
]
}
]
OrderController
$user = Auth::user();
return response()->json($user->deliveredOrders()->with('items')->get());
User Model
public function deliveredOrders()
{
return $this->hasMany(Order::class)->where('status', '=', 'delivered');
}
Order Model
public function items()
{
return $this->hasMany(OrderItem::class);
}
You should create this relationship in the OrderItem model
public function food()
{
return $this->belongsTo('App\Food'); //or wherever you have the model
}
and then get it
$user = Auth::user();
return response()->json($user->deliveredOrders()->with('items.food')->get());
You should define food() method on OrderItem model.
public function food()
{
return $this->belongsTo('App\Food');
}
And get relation data using this:
$user = Auth::user();
return response()->json($user->deliveredOrders()->with('items.food')->get());
I am trying to sort records order by highscore desc that I get top high scored users. I have tried a lot and still trying but could not achieve the task.
Following is working but it does sorting
$GameLog = User::with(['games' => function ($q){
$q->withPivot('highscore','level');
}])->get();
I also tried following to achieve it but this is also not working
$GameLog = User::with(['games' => function ($q){
$q->withPivot('highscore','level');
}])->groupBy(DB::raw('highscore DESC'))
->get();
I also tried sortBy() function in the loop but still I am facing issue. The first query is returning following result which should be sort by highscore
{
"status": "success",
"data": [
{
"id": 1,
"name": "fasdfsad",
"games": [
{
"id": 1,
"pivot": {
"highscore": 506,
}
}
]
},
{
"id": 8,
"name": "john",
"favorite_game_id": null,
"games": [
{
"id": 1,
"pivot": {
"highscore": 2340,
}
}
]
},
{
"id": 10,
"name": "tfyuyu",
"games": [
{
"id": 1,
"pivot": {
"highscore": 100,
}
}
]
}
]
}
Can someone kindly give me any idea how can I fix it I would appreciate. Thank you so much
Edited for Models
In Game.php
public function users()
{
return $this->belongsToMany(User::class, 'user_and_game', 'game_id', 'user_id')
->withPivot('highscore');
}
In User.php
public function games()
{
return $this->belongsToMany(Game::class, 'user_and_game', 'user_id', 'game_id');
}
I would like to appreciate if someone kindly help me about that
You need to create a new model for pivot (highscore). For example:
<?php
namespace App;
use Illuminate\Database\Eloquent\Relations\Pivot;
class Highscore extends Pivot
{
public function user()
{
return $this->belongsTo(User::class);
}
public function game()
{
return $this->belongsTo(Game::class);
}
}
Note: from the example, we are extending from Pivot class instead of Model.
In the controller you can sort it like this example:
$highscores = Highscore::orderByDesc('score')
->with('user', 'game')
->get();
When using a custom pivot table, you need to define it from User and Games table.
public function users()
{
return $this->belongsToMany(User::class, 'user_and_game', 'game_id', 'user_id')
->using(Highscore::class)
->withPivot('highscore');
}
class Order extends Elequent{
protected $table = 'order';
const CREATED_AT = "createdAt";
const UPDATED_AT = "updatedAt";
protected $with = array('orderItem', 'address');
public function orderItem(){
return $this->hasMany("OrderItem","orderId");
}
public function address(){
return $this->hasOne("OrderAddress","orderId","id");
}
}
//in controller
$order = Order::findOrFail($orderId);
echo $order;
When serialize the order to json using above class,how can I make the order_item to orderItem(I like camel case more than snake case)
{
"id": 1,
"userId": 1,
"createdAt": "2015-04-30 12:08:26",
"deletedAt": "0000-00-00 00:00:00",
"updatedAt": "2015-04-30 12:08:26",
"state": "activated",
"deliveryNote": null,
"order_item": [
{
"id": 1,
"specialProductId": 29,
"volume": 750,
"quantity": 2,
"price": "543.38",
"orderId": 1,
"updatedAt": "2015-04-30 12:08:26",
"createdAt": "2015-04-30 12:08:26"
}
],
"address": null
}
In your model, set the $snakeAttributes property to false. This will keep the attributes from being snake_cased when generating the array.
class Order extends Elequent {
public static $snakeAttributes = false;
}
You can copy/rename the object property and unset the old one.
//in controller
$order = Order::findOrFail($orderId);
$order->orderItem = $order->order_item;
unset($order->order_item);
echo $order;
For better usage, you can put this method on your Base model,
public function toArrayCamel()
{
$array = $this->toArray();
foreach($array as $key => $value)
{
$return[camel_case($key)] = $value;
}
return $return;
}
and later on your controller,
$order = Order::findOrFail($orderId);
return Response::json( $order->toArrayCamel() );
My Message model has many MessageAttachment
Following code:
$message = Message::find(1);
return $message->attachments;
Outputs:
[
{
"id": 1,
"message_id": 1,
"attachable_id": 1,
"attachable_type": "Item",
},
{
"id": 2,
"message_id": 1,
"attachable_id": 1,
"attachable_type": "Photo",
}
]
Now, while this is all great, I'd like a way to fetch the model, the MessageAttachment is referring to (i.e. Photo, or Item). Something like:
return $message->attachments[0]->attachable; // outputs the Item 1 model
This is where I get stuck. How would you do that, in a clean and simple way, using above structure, or something similar?
this should work:
$messages = Message::find(1);
foreach($messages as $key => $value){
$model = $value->attachable;
$array[$key] = $model::find($value->attachable_id);
}
dd($array);
Simple. The solution:
class Message extends \Eloquent {
public function attachments() {
return $this->hasMany('MessageAttachment');
}
}
class MessageAttachment extends \Eloquent {
public function attachable() {
return $this->morphTo();
}
}
Doing:
$message = Message::find(1);
$message->attachments[0]->attachable; // returns the model
Best practice is to fetch the items directly with the realtions. You can do this like this:
Messages::with('attachments', 'attachments.photo')->get();
Here photo is a relation of attachments. All relations will be fetched. If you have a single message you can access the relation by:
$message->attachments->first()->photo