Laravel - How to Access and Manipulate Pivot Table - php

I have two tables, users and like_categories which has many-to-many relationship. The pivot table is called like_category_user. After inserting two users data into the db, here is my pivot table look like:
https://i.imgur.com/MeeRbiV.png
Im a little bit confused on how can i access the pivot table since I didnt create a custom model for that pivot table. I want to count the amount for each of the different like category for each user and store it in object array like this:
[
{
"User Id": 1,
"Like Categories": [
{
"Category": Chinese Restaurant
"Amount": 1
},
{
"Category": Korean Restaurant
"Amount": 2
},
{
"Category": Fast Food Restaurant
"Amount": 3
},
{
"Category": Italian Restaurant
"Amount": 1
},
{
"Category": Steakhouse Restaurant
"Amount": 3
}
]
},
{
"User Id": 2,
"Like Categories": [
{
"Category": Thai Restaurant
"Amount": 1
},
{
"Category": Kebab Shop
"Amount": 3
},
{
"Category": Pizza Place
"Amount": 2
},
{
"Category": Steakhouse
"Amount": 1
}
}
}
]
My process method:
public function showUserLikesData() {
//
}

Try following this explanation, according to your code.
There is no real need to create a model for this table. Basically you will need to create a function in your models to have access to this table.
In your view's or your api, you will need to mention this function along with Pivot, but the field. Something like:
$showUserLikesData->pivot->FieldName

In your relationship you can add ->withPivot('column_name'), which allows you to use more than foreign keys from the pivot table. So, an example:
public function things(){
return $this->belongsToMany('App\ModelName')->withPivot('column_name')->withTimestamps();
}

Related

In Laravel, how to query the parent model according to the the sum of child fields?

I have two models, Game and Round. A game has many rounds, and a round belongs to a game. The structure is something like the following.
{
"game":{
"title": "Game One",
"description": "A game one",
"rounds": [
{
"title": "Round 1",
"points": 10
},
{
"title": "Round 2",
"points": 10
},
{
"title": "Round 3",
"points": 10
}
]
}
}
I want to query the game according to the total points from all the rounds. So for the above example, the total points would be 30. I am using the jenssegers/laravel-mongodb package and would like to know how I can achieve that?
please try the below code:
Please first add the below code in the Game modal for the relationship with the Round modal
public function round() {
return $this->hasMany(Round::class,'game_id','id');
}
Then add below function in your controller
public function getGame()
{
return Game::with('round')
->select("games.*",DB::raw("sum(rounds.points) as total_point"))
->groupBy('games.id')
->leftJoin('rounds','rounds.game_id','=','games.id')
->get();
}

How to get products category wise in Laravel?

A similar question was asked here.
I am working on APIs. My products table contains more than ten thousand products of different categories. I need to decrease query execution time, so for that purpose, I've to modify an API that will fetch only 20 products of each category and group the whole API response category-wise. The API response will be like the following.
{
"data": {
"products": {
"Beauticians & Style": [
{
"Title": "Product A",
"Price": "0.00",
"DiscountPrice": 0
},
{
"Title": "Product B",
"Price": "0.00",
"DiscountPrice": 0
}
],
"Groceries": [
{
"Title": "Product G",
"Price": "0.00",
"DiscountPrice": 0
},
{
"Title": "Product R",
"Price": "0.00",
"DiscountPrice": 0
},
{
"Title": "Product O",
"Price": "0.00",
"DiscountPrice": 0
},
{
"Title": "Product C",
"Price": "0.00",
"DiscountPrice": 0
}
],
"Women's Fashion": [
{
"Title": "Product W",
"Price": "0.00",
"DiscountPrice": 0
},
{
"Title": "Product O",
"Price": "0.00",
"DiscountPrice": 0
},
{
"Title": "Product M",
"Price": "0.00",
"DiscountPrice": 0
}
]
}
}
Controller
$products = Category::with('products', function($q){
$q->take(20);
})->get();
Category Model
class Category extends Model
{
use HasFactory;
public function products()
{
return $this->hasMany('App\Models\Product', 'CategoryID', 'CategoryID');
}
}
I've tried this but not getting the exact result.
When accessing Eloquent relationships as properties, the related models are "lazy loaded". This means the relationship data is not actually loaded until you first access the property.
The solution, you can use:
$products = Category::with('products')
->get()
->map(function ($query) {
$query->setRelation('products', $query->products->take(20));
return $query;
});
Ref :
"This is not a bug, it's a limitation"
Update
OP comment :
But in this case we are fetcing all 10k products then set relation on collection
Yes, this is performance issue, but, your question is about eadger. This is limitation.
Eloquent as a tool, not a solution. It's just a wrapper on SQL. If you wanted to limit the number of products per category to (n), the query becomes complex.
To solve this issue you will need to use window function :
SELECT *
FROM (
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY `category_id`) AS row_num
FROM `products`
) AS temp_table
WHERE row_num <= 10
ORDER BY row_num
Alternatively, you can use this Laravel Eloquent extension to allow limiting the number of eager loading results per parent using window functions.
Category Model
use Staudenmeir\EloquentEagerLimit\HasEagerLimit;
use App\Models\Product;
class Category extends Model
{
use HasEagerLimit;
public function products()
{
return $this->hasMany(Product::class);
}
}
Product Model
use Staudenmeir\EloquentEagerLimit\HasEagerLimit;
class Product extends Model
{
use HasEagerLimit;
}
Try
$products = Category::with(['products' => function ($query) {
$query->latest()->limit(20);
}])->get();
Does this explain enough to you?
if each product have only one category, your tables structure must be as below:
products
id
...
category_id
categories
id
...
and your products relation in Category must be as below:
class Category extends Model
{
use HasFactory;
public function products()
{
return $this->hasMany(App\Models\Product::class);
}
}
and change your controller to:
$products = Category::with('products', function($q){
$q->take(20);
})->get();

laravel dynamic field from linked model, based on another model

I am in a situation where I want to get the form for each team of based on competition
Team model
public function standing()
{
return $this->hasMany(Standing::class);;
}
Competition model
public function standing()
{
return $this->hasMany(Standing::class);;
}
Each Team has many Standing
Each Competition has many Standing
standing table
team_id
competition_id
form
my code
$f = Fixture::where('status', 'NS')
->with('teamA', 'teamB', 'competition')
->get();
since all fixtures of all competitions are coming, but once team can be in two competition,
hence its form is different.
[
{
"name": "Leicester vs Newcastle",
"team_a": {
"id": 10,
"name": "Leicester",
**"form": null**
},
"team_b": {
"id": 2,
"name": "Newcastle",
**"form": null**
},
"competition_id": 1,
"competition": {
"id": 1,
"name": "Premier League",
},
},
{
"name": "Leeds vs Tottenham",
"id": 344,
"team_a": {
"id": 19,
"name": "Leeds",
"form": null
},
"team_b": {
"id": 11,
"form": null
},
"competition_id": 2,
"competition": {
"id": 1,
"name": "Other League",
},
},
]
I have tried to explain the situation,
can someone please tell me how to get this?
for ex.
in champions league(competition), man. united has form WWWLDW, but same team in another league might be different

Custom sorting in Elasticsearch

Does anyone know if it's possible to custom sort in elasticsearch?
I have a sort on the category field. Which groups all of the records together by category. This works great.
However could you then give the sort a list e.g cars, books, food.
It would then show the cars first, then books and finally food?
You can use a function_score query, something like this:
{
"query": {
"function_score": {
"query": { "match_all": {} },
"boost": "5",
"functions": [
{
"filter": { "match": { "category": "cars" } },
"weight": 100
},
{
"filter": { "match": { "category": "books" } },
"weight": 50
},
{
"filter": { "match": { "category": "food" } },
"weight": 1
}
],
"score_mode": "max",
"boost_mode": "replace"
}
}
}
Where you, of course, put whichever query you are using now instead of the match_all query, and leave off the sort (the default is by score, which is what you want here).
This is replacing the score elasticsearch normally generates, with a custom score for each category. You could experiment with other boost_mode in order to have a reasonable ranking within the categories. In case you need to understand what is happening with the scoring, you can add "explain": true to the query at the top level.
You can use custom script for your own scoring.
More details at in Script Based Sorting section: https://www.elastic.co/guide/en/elasticsearch/reference/5.5/search-request-sort.html

Laravel usage of GroupBy

I'm trying to group data in a table by cat_id and convert the result into JSON.
Table structure
id name cat_id
1 A 2
2 B 1
3 C 2
4 D 2
5 E 3
6 F 2
7 G 1
Here is the code I tried
$posts = Posts::groupBy('cat_id')->get();
return $posts->toJson();
Result should look like this:
[
{
"id": "1", //cat_id
"posts": [
{
"id": "2", //user id
"s": "B" //name
},
{
"id": "7",
"s": "G"
}
]
},
{
"id": "2", //cat id
"posts": [
{
"id": "1",
"name": "A"
},
{
"id": "3",
"name": "C"
},
{
"id": "4",
"s": "D"
},
{
"id": "6",
"s": "F"
}
]
}
]
But I'm getting Call to a member function toJson() on a non-object. Where am I doing wrong?
Unfortunately groupBy doesn't work like this. It's more like SQLs GROUP BY (actually that's what it does in the background...)
I assume your error comes from the fact that it doesn't know what to do with name and id because you always need aggregate fields with GROUP BY.
Now here's how you can achieve your goal. I assume you have set up the relation between Category and Post. somewhat like this:
class Category extends Eloquent {
public function posts(){
return $this->hasMany('Post', 'cat_id');
}
}
Then you are able to do:
$categories = Category::with('posts')->get();
And you get all the categories containing its posts. toJson() should work without a problem as well.
#lukasgeiter suggested right thing to do, however there's another way to go too. Depending on your needs you can choose the one for you:
$posts = Pots::all(); // fetch from the DB
$groupedByCat = $posts->groupBy('cat_id'); // call group by on the collection
Result will look like this:
{
"1": // cat_id
[
{
"id": "5" // post data
...
},
...
],
"2":
...
}

Categories