Laravel Eloquent select distinct from relationship - mysql

I have a many to many relationship between categories and products tables.
┌─────────────────┐ ┌─────────────────┐ ┌──────────────────┐
│ categories │ │ products │ │ category_product │
├─────────────────┤ ├─────────────────┤ ├──────────────────┤
| id | | id | | shop_id |
| name | | name | | category_id |
| created_at | | price | | product_id |
└─────────────────┘ | created_at | | created_at |
└─────────────────┘ └──────────────────┘
Category model:
class Category extends Model
{
public function products()
{
return $this->belongsToMany(Product::class);
}
}
Product model:
class Product extends Model
{
public function categories()
{
return $this->belongsToMany(Category::class);
}
}
Because I'm using the cetegory_product table to also map the shop. I have some problem with getting the distinct results. let's just say that I have the following records in the pivot table:
shop_id | category_id | product_id
--------------------------------------
1 |18 |4
1 |18 |5
Now when I try to get the categories for a shop or the reverse relationship like this:
$shop = Shop::find(1);
$shop->categories()->get();
I get duplicated data.
So I like to know how can I perform a distinct select for categories?
BTW: I tried to add a scope on each model, adding the distinct to the query, but it did not work.

Unless I'm missing something you should be able to use groupBy().
$shop = Shop::find(1);
$shop->categories()->groupBy('category_id')->get();

$shop->categories()->distinct()->get();

Related

How to multiply and group using laravel eloquent with relation

I'd like to export data from my database but have problems with multiplying and sum using laravel eloquent with relation
So i have 2 tables there (budgets, items)
Budget's:
// Table
+----+---------------+-----------------+------+-----+--------------------+
| id | delivery_plan | item_code | curr | qty | price |
+----+---------------+-----------------+------+-----+--------------------+
| 1 | 2022-08 | 201.0001 | IDR | 1 | 2000.0000000000 |
| 2 | 2022-08 | 201.0001 | IDR | 3 | 2000.0000000000 |
| 3 | 2022-07 | 201.9999 | IDR | 2 | 2000.0000000000 |
+----+---------------+-----------------+------+-----+--------------------+
// Relation
public function item()
{
return $this->belongsTo(Item::class, 'item_code', 'item_code');
}
Items :
// Table
+----+----------------+-----------+
| id | subgroup | item_code |
+----+----------------+-----------+
| 1 | KOMPONEN MESIN | 201.0001 |
| 2 | EQUIPMENT LAIN | 201.9999 |
+----+----------------+-----------+
// Relation
public function budgets()
{
return $this->hasMany(Budget::class, 'item_code', 'item_code');
}
So, the scenario is :
Multiply the "qty" * "price" columns and name them as "total" like so
Group them by "subgroup" column, which came from item() relationship
Group them by "delivery_plan"
I prefer using eloquent because to minimize the complexity because i need that "whereHas" method
This is what i've tried so far and isn't working :
$budgets = Budget::with('item', 'rate')->whereHas('period.term', function (Builder $builder) {
$builder->where('name', '=', Session::get('term-budget'));
})->where('section', Session::get('section-budget'))->getQuery();
$result = $budgets->sum('price * qty')->get();
How can i achieve this ?
This can be solved by a join with SUM(), something like below (untested):
Budget::leftJoin('items', 'budgets.item_code', '=', 'items.item_code')
->addSelect('subgroup')
->addSelect('delivery_plan')
->addselect(\DB::raw('SUM(qty * price) as total'))
->groupBy('subgroup', 'delivery_plan')
->get();

Mysql / Laravel: Search keywords by multi languages but only get the results of current language

"language" table
id | name | code
-------------------
1 | English | en
2 | German | de
"article" table
id | is_active
--------------
1 | 1
2 | 1
"article_translation" table
article_id | language_id | title
-------------------------------------------
1 | 1 | foo
1 | 2 | title1 in German
2 | 1 | title2 in English
2 | 2 | foo
Suppose I search "foo" with like condition, and I want to get the results with language_id = 1 but still need to search the title on other languages. The following results as I expected:
article_id | language_id | title
-------------------------------------------
1 | 1 | foo
2 | 1 | title2 in English
In laravel, I tried to use group by function but the results can't make sure that the language_id is 1. Also, it need to set the strict mode to false.
\App\ArticleTranslation::where('title', 'like', '%foo%')->groupBy('article_id')->paginate(10);
SELECT at1.*
FROM article_translation at1
JOIN article_translation at2 ON at1.article_id = at2.article_id
WHERE at2.title = #title
AND at1.language_id = #language_id
fiddle
PS. Convert to Laravel by yourself...
Add relations for easy queries
// Language model
public function articles()
{
return $this->belongsToMany(Article::class);
}
// Article model
public function languages()
{
return $this->belongsToMany(Language::class);
}
// in controller
$articles = Language::find(1)->articles()->wherePivot('title', 'like', '%foo%')->get();
foreach($articles as $article){
dump($article); // $title = $article->pivot->title
}

Make a users list like instagram

please help..
I have 2 tables
Current user_id = 3
Users Table:
| user_id | email | name |
-------------------------------------------
| 1 | one#gmail.com | ridwan |
| 2 | two#gmail.com | budi |
| 3 | six#gmail.com | stevan |
| 4 | ten#gmail.com | agung |
Relations Table [ user_id and follower_id are related to Users Table ]
| relation_id | user_id | follower_id |
-----------------------------------------
| 1 | 1 | 3 |
| 2 | 2 | 3 |
i want to get the list of the user, but if i already have relation with a user, it will give me a status 'following', just like instagram, maybe look like this
{
user_id : 1,
name : ridwan,
status : following
},
{
user_id : 2,
name : budi,
status : following
},
{
user_id : 4,
name : agung,
status : not following
}
how can i do that in laravel?
thank you..
In your Relations model
public function user()
{
return $this->belongsTo(User::class);
}
Then, in your controller (where you want to get this list), you just need to use Eloquent or Query Builder to get what you want (with eager loading, it's always better) :
//get all users related to your_current_user
$relations = Relation::where('follower_id', your_current_user_id)->with('user')->get();
Finally, you just have to make manipulate your data like you want, for example :
foreach($relations as $relation){
$relation->user->name; //get the name of the related user
}
Take a look at Relationships !
Thank you guys.. finally I use sql case, i was only confusing about how to get the data with 'following' status
here is the code:
DB::table('users')->leftjoin('relationships', 'users.user_id', '=', 'relationships.user_id')->select('users.user_id','users.status as user_type','users.email','users.display_name','users.profile_image',DB::raw('(CASE WHEN relationships.follower_id = ' . $user_id . ' THEN "Following" ELSE "Not Following" END) AS status'))->orderBy('user_id','asc')->where('users.user_id','!=',$user_id)->get();

Laravel5: Eloquent and JOIN

Items Table
| id | item_id | item_title |
|-------|---------|------------|
| 1 | 1002 | A |
| 2 | 1003 | B |
| 3 | 1004 | C |
Sells Table
| id | item_id |
|----|-----------|
| 1 | 1002 1003 |
| 2 | 1003 1004 |
| 3 | 1004 1002 |
I want result : Sells Table 1. item title is A B
I want to combine the sells table with the item table and then match the item_id of the sells table to the item_title of the item table.
The table definitions look incorrect, you should have a pivot table linking items with sells, so a sell_item table:
item_id | sell_id
-----------------
1 | 1
1 | 3
2 | 1
2 | 2
3 | 2
3 | 3
Then using eloquent, you'd create models to represent your tables and define the relationships using BelongsToMany:
class Item extends Model {
public function sells() {
return $this->belongsToMany(Sell::class);
}
}
class Sell extends Model {
public function items() {
return $this->belongsToMany(Item::class);
}
}
Each instance of either model will then have access to it's related models via $item->sells and $sell->items.
The query builder can perform a join if not going the Eloquent route:
DB::table('sells')->join('items', 'sells.item_id', '=', 'items.item_id')
->select('sells.*', 'items.title')
->get();
The table definitions look incorrect, If you corrected already then your model replationship should be like
class Item extends Model {
public function sells() {
return $this->belongsToMany(Sell::class);
}
}
class Sell extends Model {
public function items() {
return $this->belongsToMany(Item::class);
}
}
Each instance of either model will then have access to it's related models via $item->sells and $sell->items.
The query builder can perform a join if not going the Eloquent route:
DB::table('sells')->join('items', 'sells.item_id', '=', 'items.item_id')
->select('sells.*', 'items.title')
->get();
Or if your model name is Sell then
$response=Sell::with('items')->get();

Loading a polymorphic model recursive with conditions

I try to eager load a polymorphic model with some conditions that's connected to two other models and having some problems.
My models:
class One extends Model {
public function twos() {
return $this->hasMany(Two::class);
}
public function threes() {
return $this->morphMany(Three::class, 'threeable');
}
}
class Two extends Model {
public function one() {
return $this->belongsTo(One::class);
}
public function threes() {
return $this->morphMany(Three::class, 'threeable');
}
}
class Three extends Model {
public function threeable() {
return $this->morphTo();
}
}
So far everythings is everything great: I've a lot of One items that have a lot of Two relations and both have even more Three relations (on its own).
Now I try to get the latest Three from Two, where it also can come from a One relation (highest threes.updated_at from either A or B).
The threes table looks something like this
| id | threeable_id | threeable_type | updated_at |
|---|---|---|---|
| 1 | 1 | A | 1 |
| 2 | 1 | A | 2 |
| 3 | 1 | B | 3 |
| 4 | 1 | B | 4 |
| 5 | 2 | A | 2 |
| 6 | 2 | A | 4 |
| 7 | 2 | B | 1 |
| 8 | 2 | B | 3 |
I want threes.id = 4 for B::find(1)->withC() and threes.id = 6 for B::find(2)->withC().
For performance reasons I want to load it in one query (I'm loading multiple Two and want the related Three and don't want to fire an own query for that). So I tried to put it in a scope (in class Two). Joining on the Three table and doing some max on the updated_at in a sub query ... It was a total mess and didn't worked that well
It didn't really feel the "Laravel-way" neither :(
Thanks for any help
EDIT
Maybe as an addition here's the pure SQL
SELECT twos.id, threes.id
FROM
(SELECT
twos.id twoId, MAX(th.updated_at) threeMaxUA
FROM
twos
LEFT JOIN (SELECT
th.*
FROM
(SELECT
threeable_id, threeable_type, MAX(updated_at) AS updated_at
FROM
threes
WHERE
deleted_at IS NULL
GROUP BY threeable_id , threeable_type) thmax
LEFT JOIN
threes pr ON (th.threeable_id = thmax.threeable_id
AND th.updated_at = thmax.updated_at
AND th.threeable_type = thmax.threeable_type)
GROUP BY th.threeable_id , th.threeable_type) th
ON
(
(th.threeable_id = twos.id AND th.threeable_type = 'App\\Two')
OR
(th.threeable_id = twos.product_id AND th.threeable_type = 'App\\One')
)
GROUP BY twos.id
) twthmax
LEFT JOIN twos ON twos.id = twthmax.twoId
LEFT JOIN threes ON (
twthmax.threeMaxUA = threes.updated_at
AND
(
(threes.threeable_id = twos.id AND threes.threeable_type = 'App\\Two')
OR
(threes.threeable_id = twos.product_id AND threes.threeable_type = 'App\\One')
)
)