I have 3 tables and I want to join 2 of them through another using eloquent
ProductGroup table
--------------------------
| ProductGroupId | Name |
-------------------------
| 1 | Test1 |
| 2 | Test2 |
--------------------------
ProductLine table
-------------------------------------------------
| ProductLineId | ProductGroupId | Name |
------------------------------------------------|
| 1 | 1 | AnotherTest1 |
| 2 | 1 | AnotherTest2 |
-------------------------------------------------
ProductType table
----------------------------------------------
| ProductTypeId | ProductLineId | Name |
---------------------------------------------|
| 1 | 1 | Something1 |
| 2 | 1 | Something2 |
----------------------------------------------
I want to join ProductGroup with ProductType
I tried using this as my ProductGroup model (not sure if I've done the hasManyThrough() correctly)
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class ProductGroup extends Model
{
protected $primaryKey = 'ProductGroupId';
public $timestamps = false;
public function producttype()
{
return $this->hasManyThrough('App\ProductGroup', 'App\ProductLine', 'ProductGroupId', 'ProductGroupId');
}
}
I want to join the 2 tables on a particular id from the ProductGroup table so really in SQL it would be
SELECT * FROM ProductType pt
INNER JOIN ProductLine pl ON
pt.ProductLineId = pl.ProductLineId
INNER JOIN ProductGroup pg ON
pg.ProductGroupId = pl.ProductGroupId
WHERE pg.ProductGroupId = 3
I have tried this but I get no results
I could do this in query builder but i'd rather use eloquent if it can be helped
$test = ProductGroup::with('producttype')->whereHas('producttype', function ($query) {
$query->where('ProductGroup.ProductGroupId', 3);
})->get();
Change
public function producttype()
{
return $this->hasManyThrough('App\ProductGroup', 'App\ProductLine',
'ProductGroupId', 'ProductGroupId');
}
to
public function producttype()
{
return $this->hasManyThrough('App\ProductType', 'App\ProductLine', 'ProductGroupId', 'ProductLineId');
}
Related
I have a business logic where I want to retrieve translations from the database, The translations can be overridden so overridden translations should be retrieved when available.
Schema:
i18n
-----
id
slug // unique
i18nTranslations
--------------
id
i18nId // referencing i18n.id
langId
text
overriddenType // pageOverride / instanceOverride
i18nPageOverrides
-----------------
id
translationId // referencing i18nTranslations.id
instanceId
pageId
Example:
i18nTranslations
------------------------------------------------------
id i18nId langId text type overrideType
------------------------------------------------------
1 | ABC | En | AAX | static |
2 | ABC | En | AAX Ovd | static | pageOverride
3 | ABC | Tr | TDF | static |
i18nPageOverride
--------------------------
transId pageId instanceId
--------------------------
2 login admin
Expected Output:
------------------------------------------------------
id i18nId langId text type overrideType
------------------------------------------------------
2 | ABC | En | AAX Ovd | static | pageOverride // overridden data
3 | ABC | Tr | TDF | static |
In the expected output above, The row with "AAX" text has been eliminated since it had overridden row for the lang.
Is there any way to achieve this behavior just by using a query?
A DISTINCT ON expression with an ORDER BY could be perfect for this.
The sorting can be on a descending i18nPageOverrides.id, with the nulls sorted last.
DISTINCT ON ( expression [, ...] ) keeps only the first row of each
set of rows where the given expressions evaluate to equal. The
DISTINCT ON expressions are interpreted using the same rules as for
ORDER BY (see above).
SELECT DISTINCT ON (tr.i18nId, tr.langId)
tr.id, tr.i18nId, tr.langId, tr.itText, tr.itType, tr.overrideType
FROM i18nTranslations tr
LEFT JOIN i18nPageOverrides po ON po.translationId = tr.id
ORDER BY tr.i18nId, tr.langId, po.id DESC NULLS LAST, tr.id;
id
i18nid
langid
ittext
ittype
overridetype
2
ABC
En
AAX Ovd
static
pageOverride
3
ABC
Tr
TDF
static
null
Test on db<>fiddle here
You can use ROW_NUMBER window function PARTITION BY and ORDER BY to make a row number for duplicate number then filter rn = 1 rows.
Query 1:
SELECT "iId",
"itI18nId",
"itLangId",
"itText",
"itType",
"itOverrideType"
FROM (
SELECT *,
ROW_NUMBER() OVER(PARTITION BY "itI18nId","itLangId" ORDER BY "itOverrideType","iCreatedAt") rn
FROM i18n i
INNER JOIN i18n_translations t
ON i."iId" = t."itI18nId"
LEFT JOIN i18n_page_override o
ON o."ipoTranslationId" = t."itId"
) t1
WHERE rn = 1
Results:
| iId | itI18nId | itLangId | itText | itType | itOverrideType |
|--------------------------------------|--------------------------------------|--------------------------------------|--------|--------------|----------------|
| b76481bc-1171-4fb3-8433-31302ae39a81 | b76481bc-1171-4fb3-8433-31302ae39a81 | 175376f6-9dc8-4bea-bbc0-bf93744999c9 | Adi | staticNormal | (null) |
| b76481bc-1171-4fb3-8433-31302ae39a81 | b76481bc-1171-4fb3-8433-31302ae39a81 | 875dbdbb-9cb2-4f1b-a8ca-096321a0cd36 | Fn Ovd | staticNormal | stPageOverride |
So I have looked around the internet, and couldn't find anything that could be related to my issue.
This is part of my DB:
ID | English | Pun | SID | Writer |
=======================================================
1 | stuff | stuff | 1 | Full |
2 | stuff | stuff | 1 | Rec. |
3 | stuff | stuff | 2 | Full |
4 | stuff | stuff | 2 | Rec. |
Now how would I get all rows with SID being equal to 1.
Like this
ID | English | Pun | SID | Writer |
=======================================================
1 | stuff | stuff | 1 | Full |
2 | stuff | stuff | 1 | Rec. |
Or when I want to get all rows with SID being equal to 2.
ID | English | Pun | SID | Writer |
=======================================================
3 | stuff | stuff | 2 | Full |
4 | stuff | stuff | 2 | Rec. |
This is my current SQL Query using SQLite:
SELECT * FROM table_name WHERE SID = 1
And I only get the first row, how would I be able to get all of the rows?
Here is my PHP Code:
class GurDB extends SQLite3
{
function __construct()
{
$this->open('gurbani.db3');
}
}
$db = new GurDB();
$mode = $_GET["mode"];
if($mode == "2") {
$shabadnum = $_GET["shabadNo"];
$result = $db->query("SELECT * FROM table_name WHERE SID = $shabadnum");
$array = $result->fetchArray(SQLITE3_ASSOC);
print_r($array);
}
Fetch array only gives you one row... you want something like this:
while($row = $result->fetch_array())
{
$rows[] = $row;
}
I'm trying to join more than 2 tables using 1 same key reference,
it works fine when I join two 2 tables using left join like this :
SELECT userProfile.UserProfileID, userProfile.name, jobPosition.levelname
FROM master.eliUserProfile userProfile
LEFT JOIN master.eliJobPosition jobPosition ON userProfile.UserProfileId = jobPosition.UserProfileID
where userProfile.UserProfileId = '5001'
Result :
userProfileid | name | levelName |
-----------------+---------+--------------+
5001 | dirdple | Direktur PLE |
5001 | dirdple | Direktur PLE |
But when I want to join another table into it, it return double values, I am using the master table reference (which is UserProfile) :
SELECT userProfile.UserProfileID, userProfile.name, jobPosition.levelname, userAcmMapping.UserAcmMappingId, userAcmMapping.RefID
FROM master.eliUserProfile userProfile
LEFT JOIN master.eliJobPosition jobPosition
ON userProfile.UserProfileId = jobPosition.UserProfileID
LEFT JOIN transactions.eliUserAcmMapping userAcmMapping
ON userProfile.UserProfileId = userAcmMapping.UserProfileID
where userProfile.UserProfileId = '5001'
and it turned out to resulted like this :
userProfileId | name | levelName | UserAcmMappingId | refId |
-----------------+---------+--------------+------------------+-------+
5001 | dirdple | Direktur PLE | 1 | 21 |
5001 | dirdple | Direktur PLE | 7 | 22 |
5001 | dirdple | Direktur PLE | 158 | 23 |
5001 | dirdple | Direktur PLE | 1 | 21 |
5001 | dirdple | Direktur PLE | 7 | 22 |
5001 | dirdple | Direktur PLE | 158 | 23 |
Is it because Iam using the same key(userProfile.UserProfileId)? Fyi, the 3rd table (userAcmMapping) have 3 values with UserProfileId=5001 and 2nd table (jobPosition) have 2 values with UserProfileId=5001.
And the table I want to be referenced is the second table with 2 values (userProfileId=5001)..
Thank you for any help..
It is because you are joining two different tables to one table with the same key. You are joining jobPositions and userAcmMapping to userProfile. I mean there are two joins on one table with the same field, so the result is doubled.
You should instead join jobPositions to userProfile and join userAcmMapping to jobPositions, like this:
SELECT userProfile.UserProfileID, userProfile.name, jobPosition.levelname, userAcmMapping.UserAcmMappingId, userAcmMapping.RefID
FROM master.eliUserProfile userProfile
LEFT JOIN master.eliJobPosition jobPosition
ON userProfile.UserProfileId = jobPosition.UserProfileID
LEFT JOIN transactions.eliUserAcmMapping userAcmMapping
ON jobPosition.UserProfileID = userAcmMapping.UserProfileID
where userProfile.UserProfileId = '5001'
Here it is one join per table.
Note: I didn't test the code, since I don't have actual tables, but it should work.
How do I get the last price claimed by the retailer b#gmail.com (the 5th row in the price table) with the condition that the role = 'retailer' ?
So here is my simple table:
table users (using Entrust Role package with the default relationship in the model)
__________________________
| id | email | password |
|-------------------------|
| 1 | a#g.com | 123 |
| 2 | b#g.com | 123 |
| 3 c#g.com | 123 |
| 4 d#g.com | 123 |
--------------------------
table roles (using Entrust Role package with the default relationship in the model)
______________
|id | name |
|--------------|
|1 | customer |
|2 | retailer |
----------------
table role_user (using Entrust Role package with the default relationship in the model)
__________________
|id_user | id_role|
|------------------|
| 1 | 1 | -> a#gmail.com is a customer
| 2 | 2 | -> b#gmail.com is a retailer
| 3 | 1 | -> c#gmail.com is a customer
| 4 | 1 | -> d#gmail.com is a customer
------------------
Here is the tricky part how to query the price:
I have the following prices table (Users can post 1 or more prices. Look at the relationship below):
____________________
|id| user_id| price |
|--------------------|
|1 | 1 | 10.00 | -> price claimed by a customer a#gmail.com
|2 | 2 | 5.00 | -> price claimed by a retailer b#gmail.com
|3 | 1 | 6.00 | -> price claimed by a previous customer a#gmail.com
|4 | 3 | 5.00 | -> price claimed by a customer c#gmail.com
|5 | 2 | 7.00 | -> price claimed by a previous retailer b#gmail.com //How to get this one? This is the last price claimed by the retailer.
|6 | 3 | 8.00 | -> price claim by a customer c#gmail.com
---------------------
The relationship in my Price model:
class Price extends Model{
public function product()
{
return $this->belongsTo('App\Product');
}
public function user()
{
return $this->belongsTo('App\User');
}
How do I get the last price claimed by the retailer b#gmail.com (the 5th row in the price table) with the condition that the role = 'retailer' ?
The purpose is to get the last price that the retailer has claimed.
Update my question:
I want to access the price claimed by the last retailer from my Product model using the $products variable.
A sample table product that I have:
_______________________________
|id | user_id| name |
|------------------------------
| 1 | 1 | Milk |
| 2 | 2 | Phone |
| 3 | 1 | computer |
| 4 | 1 | Banana |
------------------------------
My Product.php model relationship:
class Product extends Model{
public function prices()
{
return $this->hasMany('App\Price');
}
}
So, in my ProductController.php I send the $product variable to the view like this:
class ProductController extends Controller
{
public function show($id)
{
$product = Product::where('id', '=', $id)->
return view('products.show')->with('product', $product);
}
}
and in my view show.blade.php, I loop through the $product variables and I can display the prices claimed for the product.
#foreach($product->prices as $price)
<li>Price claimed: {{$price->price. " " }} </li>
#endforeach
I want to get like something like
$price_last = $product->prices()->where(role, 'retailer')->last().
dd($price_last);
The last() function there is the last price that the retailer claimed but this code is just example. How do I achieve this?
Please let me know if you need more information.
You're looking for the whereHas method:
$query = Price::latest('id')->whereHas('user', function ($query) {
$query->whereHas('role', function ($query) {
$query->where('name', 'retailer');
});
});
$price = $query->value('price');
This assumes you've set up the relationships between your User, Role and Price models.
This HQL query has been driving me up a wall, and I hope someone can help me out. Here is my data model:
public class Record {
private int id;
private String name;
private Set<RecordFieldData> recordFieldData;
}
public class RecordFieldData {
private int id;
private String data;
private Record record;
private RecordTypeField type;
}
public class RecordTypeField {
private int id;
private String dataType;
}
Here is some data:
Record
-------------------------------------------------
| id | name |
-------------------------------------------------
| 1 | Abc |
| 2 | 123 |
| 3 | Xyz |
-------------------------------------------------
RecordFieldData
-------------------------------------------------
| id | record_id | data | type_id |
-------------------------------------------------
| 1 | 1 | Blue | 1 |
| 2 | 1 | Round | 2 |
| 3 | 2 | Red | 1 |
| 4 | 2 | Square | 2 |
| 5 | 3 | White | 1 |
| 6 | 3 | Oval | 2 |
-------------------------------------------------
RecordTypeField
-------------------------------------------------
| id | dataType |
-------------------------------------------------
| 1 | Color |
| 2 | Shape |
-------------------------------------------------
What I need is a list of Records that are sorted by RecordField.data of a certain type. For example, sort the Records on RecordField.data but only for RecordFieldData of type 'color'. RecordFieldData.data does not have to be returned in the query, I can get that later, but I need the sort to happen in the query that retrieves the records (otherwise pagination won't work). Keep in mind RecordFieldData of a certain type can be missing for a Record but I still want the record in the list.
I tried this query but I am getting duplicate records because it is joining RecordFieldData rows that I do not want:
SELECT r FROM Record r
LEFT JOIN r.recordFieldData AS field
LEFT JOIN field.type AS typeField WITH typeField.dataType = 'color'
ORDER BY LOWER(field.data)
Any suggestions?
EDIT
Just saw your requirement of needing to return all records. So replacing LEFT JOIN with JOIN as I initially suggested won't work.
Try using DISTINCT instead
SELECT DISTINCT r FROM Record r
LEFT JOIN r.recordFieldData AS field
LEFT JOIN field.type AS typeField WITH typeField.dataType = 'color'
ORDER BY LOWER(field.data)
EDIT 2
I think LEFT JOIN FETCH needs to be used, though I'm not sure why it gave you an error the last time. Maybe something like this
SELECT DISTINCT r FROM Record r
LEFT JOIN FETCH r.recordFieldData AS field
LEFT JOIN FETCH field.type AS typeField WITH typeField.dataType = 'color'
ORDER BY LOWER(field.data)