Create JOIN for a many-to-many relationship - php

I can't get my head around JOIN and many-to-many database relationships.
I have 3 tables (structure has been simplified)
USERS:
id (primary) | userEmail | userName | userAddress | userCreated
TEAMS:
id (primary) | teamName | teamLogo | teamCreated
TEAMS-USERS
id (primary) | userId | teamId | userLevel
Teams can have many Users. And Users can be in many Teams. The UsersTeams table tracks which users are in which teams (note: I'm not using foreign keys or anything).
When a user logs in, I want to get a list of all the teams they are a member of and the corresponding data i.e.
get the data from USERS (where id = $id)
look it up in TEAMS-USERS
get the team information from TEAMS via the teamID in TEAMS-USERS
store it all in one array
I have this code so far but to be honest I'm just trying lots of variations:
$model->select('users.*, teams.*, teams.id as teamId, teams-users.id as teamsUsersId, teams-users.userId as teamsUsersUserId, teams-users.teamId as teamsUsersTeamId');
$model->where("users.id", $id);
$model->join('teams-users','teams.id = teams-users.teamId','inner');
$data = $model->get()->getRowArray();
Running the query above gives me the error: Unknown table 'myproject.teams' - even though the TEAMS table 100% exists and I use it throughout my project.
Where am I going wrong?

You get this error because in your query you are not including a from clause and you are missing a join clause. With codeigniter you can do that like:
$model->select('users.*, teams.*, teams.id as teamId, teams-users.id as teamsUsersId, teams-users.userId as teamsUsersUserId, teams-users.teamId as teamsUsersTeamId');
//$model->where("users.id", $id);
$model->join('teams-users','teams.id = teams-users.teamId','left');
$model->join('users','user.id = teams-users.userId and users.id='.$id,'left');
$data = $model->get('teams')->getRowArray();
explanation:
there was a missing join of table users
you want to use left joins
place the where clause inside the corresponding join (it becomes and)
you missed the from part, which can be done placing the table name in
the get() function or by $model->from('teams');
see also this nice explanation of joins

Related

Laravel hasmanythrough or mysql groupby

I have three tables
category
id | category_name | created_at | updated_at
profile
id | category_id | profile_name | created_at | updated_at
photos
id | profile_id | photo_name | photo_path | photo_type | created_at | updated_at
Now i need to list photos by category and also only one photo from each profile by photo_type
I have tried using hasManyhrough but descending by created_at not working
public function categoryPhotos()
{
return $this->hasManyThrough(ProfilePhoto::class,Profile::class);
}
$response=Category::with(['categoryPhotos'=>function($query){
$query->where('profile_photos.photo_type',2);
$query->orderBy('profile_photos.created_at','ASC');
$query->groupBy('profile_photos.profile_id');
}])->whereHas('categoryPhotos')->get();
if its not possible via laravel relation then mysql query also fine for me.Thank you
Possible solution (if I understand the task correctly) may be
SELECT *
FROM category ca
JOIN profile pr ON ca.id = pr.category_id
JOIN photos ph ON pr.id = ph.profile_id
JOIN ( SELECT ph1.profile_id, MAX(ph1.id) id
FROM photos ph1
JOIN ( SELECT profile_id, MAX(created_at) created_at
FROM photos
WHERE phototype=2
GROUP BY profile_id ) sq1 ON ph1.profile_id = sq1.profile_id
AND ph1.created_at = sq1.created_at
WHERE ph1.phototype=2
GROUP BY ph1.profile_id ) sq2 ON ph.id = sq2.id
Explanations.
Subquery sq1 selects last created_at for each profile for photos with phototype=2.
Subquery sq2 takes this result and gets maximal id for photos which's profile and datetime matched those obtained in sq1 (if more than 1 photo matches the conditions, this subquery selects one of them).
FROM category ca
JOIN profile pr ON ca.id = pr.category_id
JOIN photos ph ON pr.id = ph.profile_id
collects all possible combined rows - for each category it gets all related profiles with all related photos. Then we join our subquery, which filters this total list and gets only one row for each (category, profile) pair with the photo matched our conditions (or, if none such photo, none pair is selected).
Test it in this pure SQL form (does it gives correct result? start from separate sq1 execution, check, if correct then execute sq2 and check again, and finally check the whole query). If it is correct then try to convert it to Laravel form.

Create relational tables or store serialized data in a column?

I have a users table with this structure:
user_id | name | email | mobile
And another table called products with this structure:
product_id | name | price
now I want to add wish list to my project, so that a user can add some products into his wish list (with his favorite categories. Exmaple: add a wish list category called my favorite clothes and add product_id = 55 to that category)!
Now I have 2 options:
1. Add another colum to users table called user_wish_list and then store serialized data in that column like below:
$user_wish_list = [
'user_category_1' => ['product_id_1', 'product_id_2', ....] ;
];
and store serialize($user_wish_list) in user_wish_list column.
2. create 2 tables as below:
tabe: wish_list_categories
user_id | category_id (PK) | category_title
and
tabe: wish_list_items
category_id (FK) | item_id | product_id (FK)
and finally create a relation between these tables (By SQL join)
which option is better? I personally think the second one!
one of the reasons for my choice is that I can access to data with pure SQL query
like below (I do not have to process data after fetching from database by using unserialize() ):
SELECT users.user_id, users.name, wish_list_categories.category_id,
FROM Orders
INNER JOIN Customers ON users.user_id = wish_list_categories.category_id
But what about other advantages?
Or let me ask a more general question: when we exactly use relational tables instead of use column? (consider one-to-one relationship)
Use tables. In general, don't store structured data in individual SQL values. Let the tables and foreign keys produce the structure you need.
your tables should be like:
1- users table:
user_id|user_name|Email|price
2- products table:
product_id|product_name|category_id|price
3- products_category
category_id|category_name
4- user_wish_list table:
user_id|product_id

PHP function to return data from database as table with table relationships

I have a requirement for a PHP function that takes table or tables and the required columns from those db tables and returns a html table containing the data. I know how to do this for one table but am struggling with how to make this more dynamic
My thinking for one table would be to have a function that takes the table name and then an array of columns and then just selects the data from the table and then loops through it constructing the data as html and then return that from the function.
As an example my database has two tables; users and orders
users
|----------------------------|
|user_id|first_name|last_name|
|-------|----------|---------|
orders
|----------------------|
|order_id|user_id|total|
|--------|-------|-----|
Now with the function discussed above it would be easy to generate a table for all the users or orders but what I would like to do is have a function where I could dynamically join tables and for example list all users and the number of orders they've made or list all orders from user x. I know that this would be possible with many different functions but I'm really interested in developing a way of doing this dynamically and basically building all the relationships somehow in the program and then be able to call one function and request columns x,y and z
My thinking so far would be (again for this example) somehow define that number of orders for user i = count(order_id) where user_id = i
Hope this makes sense and thank you in advance
The INFORMATION_SCHEMA.KEY_COLUMN_USAGE table can be used to find all foreign key relationships from a particular table to other tables, e.g:
SELECT `TABLE_NAME`,
`COLUMN_NAME`,
`REFERENCED_TABLE_NAME`,
`REFERENCED_COLUMN_NAME`
FROM `INFORMATION_SCHEMA`.`KEY_COLUMN_USAGE`
WHERE `TABLE_SCHEMA` = SCHEMA() -- current schema
AND `REFERENCED_TABLE_NAME` IS NOT NULL
AND `TABLE_NAME` = 'orders'; -- name of table to get relationships to other tables
This should return something like the following:
+--------------+-------------+-----------------------+------------------------+
| TABLE_NAME | COLUMN_NAME | REFERENCED_TABLE_NAME | REFERENCED_COLUMN_NAME |
+--------------+-------------+-----------------------+------------------------+
| orders | user_id | users | user_id |
+--------------+-------------+-----------------------+------------------------+
The above can be done in PHP and the results can then be iterated over to dyamically construct a query that selects from those tables, joining on the listed columns. Will leave that part as an exercise ;-)
You wouldn't need to make a function to grab data from first table then loop around them and get data from the second table.
SQL can do this for you with 1 hit on the database.
All what you need to do is join the two tables, and grab the data you want..
If I understood what you need right, you want to grab all users id from the first table, and get their order count from the second table.
A simple join or selecting from both table could do that, and I suggest something like:
Select a.user_id, b.count(order_id)
FROM table1 as a, table2 as b
WHERE a.user_id = b.user_id
Group By a.user_id
Or you could join the tables and do a similar task.
I am assuming you're gonna access database from PHP code, so try that, and give me back your feedback.
This is easy to implement but we have to fix few things.
Our requirement:
1. Identify Tables according to column name.
2. How we can Join those tables.
3. How to resolve ambiguity of columns.
Solution:
Unique column name for each field or no table has duplicate column name.
To achieve it we should have fix table prefix for each table.
for example:
your column name could be odr_orderid and usr_orderid.
Now by identifying unique prefixes, we can identify tables.
Now issue arises how to join these tables
To resolve it:
Create an another table strong JOIN keys and JOin type Left, right,inner or full.
Thats all Now you can make the query as you want.

Joining multiple tables with NULL values to avoid duplication

I am building a news feed from multiple tables status, events and tracks. The data retrieved from these tables should correspond to the user-id of all the users that I follow. On the face of it I thought this seemed simple enough and I could do this with a few joins.
Every row in each of the status, events and tracks table has unique ID and they are also unique from each other, this should make matters easier later. I have done this using a unique_id table with a primary key to retrieve ID's before inserting.
My trouble is upon joining everything together the values duplicate.
Example
If I have this data.
----------
**Status**
user-id = 1
id = 1
status = Hello Universe!
----------
**Events**
user-id = 1
id = 2
event-name = The Big Bang
----------
**Tracks**
user-id = 1
id = 3
track-name = Boom
----------
Assuming I follow user 1 I would want to retrieve this.
user-id ---- id ---- status ---- event-name ---- track-name
1 1 Hello NULL NULL
Universe
1 2 NULL The Big Bang NULL
1 3 NULL NULL Boom
But in reality what I would get is something like this.
user-id ---- status.id ---- events.id ---- tracks.id ---- status ---- event-name ---- track-name
1 1 2 3 Hello The Big Bang Boom
Universe
And that row would be repeated 3 times.
Most of the queries I have tried will get something along those lines.
SELECT * FROM users
INNER JOIN followers ON users.id = followers.`user-id`
LEFT JOIN status ON followers.`follows-id` = status.`user-id`
LEFT JOIN events ON followers.`follows-id` = events.`user-id`
LEFT JOIN tracks ON followers.`follows-id` = tracks.`user-id`
WHERE users.`id` = 2
I am using laravel, so eventually this query will be put into Eloquent format. If there is a simpler and a not performance degrading way of doing this in Eloquent please let me know.
Edit
I cannot use a UNION as there is a different number of values in each table. The example is simplified for ease of reading.
Thanks to Frazz for pointing out I could use UNIONS. I have researched into them and come up with this query.
SELECT stream.*, users.id AS me FROM users
INNER JOIN followers ON users.id = followers.`user-id`
LEFT JOIN (
SELECT `id`,`user-id`,`created_at`, `name`, NULL as status
FROM events
UNION ALL
SELECT `id`,`user-id`, `created_at`,NULL AS name, `status`
FROM status
) AS stream ON stream.`user-id` = `followers`.`follows-id`
WHERE users.id = 2
Now comes the process of converting it to an eloquent model...

Joining 2 Tables and show the same column repeatedly based on its associative ID

Hi i am not sure how to put this in a brief sentences, but i have DB table like the following
User Table
user_id
username
and so on...
Item
item_id
item_name
Item_Equipped
equipped_id head (FK to item_id)
hand (FK to item_id)
user_id (FK to user_id IN User Table)
I would like to generate a query that will display like the following format
user_id | head | head_item_name | hand | hand_item_name | ...
So far i only able to do this:
SELECT user.user_id, user.username,
equipments.head, equipments.head_acc,
equipments.hand,
equipments.acc, equipments.body
FROM gw_member_equipped AS equipments
LEFT JOIN gw_member AS user ON user.memberid = equipments.member_id
Which (i have to be brutally honest) doesn't do anything much.
I tried to perform INNER JOIN between item and item_equipped however i am unable to get individual name for each item (based on its item ID)
you need to join ITEM table two times with ITEM_EQUIPPED table.
you can use below query for your desired output column shown in question..
SELECT USER.User_Id,
Item_Equipped.Head,
Item_Heads.Item_Id Head_Item_Id, -- if you want you can remove this column
Item_Heads.Item_Name Head_Item_Name,
Item_Equipped.Hand,
Item_Hands.Item_Id Hand_Item_Id, -- and this column also as these columns are same as their previous select columns
Item_Hands.Item_Name Hand_Item_Name
FROM USER, Item Item_Heads, Item Item_Hands, Item_Equipped
WHERE USER.User_Id = Item_Equipped.User_Id
AND Item_Heads.Item_Id = Item_Equipped.Head
AND Item_Hands.Item_Id = Item_Equipped.Hand

Categories