Laravel: How to query multiple indirectly-related tables? - php

In some controller in my website I had to write all this sql query to get the results that I need since I don't think / know that Laravel Eloquent ORM can provide something very specific like this
DB::select('
SELECT users.id,
users.firstname,
users.lastname,
users.slug,
profiles.bio,
profiles.gender,
profiles.verified,
profiles.investor,
profiles.photo,
countries.name AS country,
interests.name AS interests,
specialities.name AS specialities
FROM users
JOIN profiles ON profiles.user_id = users.id
JOIN countries ON profiles.country_id = countries.id
JOIN interests_profiles ON interests_profiles.profile_id = profiles.id
JOIN interests ON interests_profiles.interest_id = interests.id
JOIN profiles_specialities ON profiles_specialities.profile_id = profiles.id
JOIN specialities ON profiles_specialities.speciality_id = specialities.id
');
However, When i return-ed the results of this query i got a very weird results where the query will return each user multiple times depending on the number of the (interests & specialities) that is associated with his profile.id
Something almost similar to this:-
---------------------------------------------------------------------
| users.id | users.firstname | ...etc... | interests | specialities |
---------------------------------------------------------------------
| 8 | Jhon | ...etc... | skydiving | Laravel |
| 8 | Jhon | ...etc... | football | JavaScript |
| 10 | Daved | ...etc... | Chatting | Physics |
| 10 | Daved | ...etc... | Driving | Engineering |
| 11 | Steve | ...etc... | Writing | Woodworks |
---------------------------------------------------------------------
So in summary what I got is that the query loops through the user many times as much as he have specialities & interests associated with his profile id.
Note that I linked the profiles table with the interests & specialities tables using pivot mid tables (interests_profiles and profiles_specialities) respectively, And I put only on them profiles_id and interest_id/speciality_id as foreign keys.
I don't know if there is any Laravel Eloquent way to get this done, because I need to filter my users based on their interests with "WHERE" clause, for example: 'WHERE intrests.name = Volleyball'?
If not, Then how to get the query to run one time only per user, so the results could be something like this:-
[{"users.id": 8, "users.firstname": 'Jhon', ...etc..., "interests":{"skydiving", "football"}, "specialities": {"Laravel", "JavaScript"}}]
And then I can loop through interests and specialities in the view.
I hope that i explained the problem well, And i apologise for prolongation.
Thanks in advance.

If you are using MySQL, you can use GROUP BY users.id AND GROUP_CONCAT, something like:
SELECT users.id,
users.firstname,
users.lastname,
users.slug,
profiles.bio,
profiles.gender,
profiles.verified,
profiles.investor,
profiles.photo,
countries.name AS country,
GROUP_CONCAT(DISTINCT interests.name) AS interests,
GROUP_CONCAT(DISTINCT specialities.name) AS specialities
FROM users
JOIN profiles ON profiles.user_id = users.id
JOIN countries ON profiles.country_id = countries.id
JOIN interests_profiles ON interests_profiles.profile_id = profiles.id
JOIN interests ON interests_profiles.interest_id = interests.id
JOIN profiles_specialities ON profiles_specialities.profile_id = profiles.id
JOIN specialities ON profiles_specialities.speciality_id = specialities.id
GROUP BY users.id
Probably you can find a way to do it also in Laravels ORM since it seems like a very flexible framework.

Related

Check if row has specific childs on join

I want to make a SQL to get the user which name is Mark and are the author of the posts with ids 1 and 3.
NOTE: It is unknown how many posts I need to check for. So it might need to generate that part of the SQL query using PHP.
How can that be done?
Users Table:
+----+----------+
| id | name |
+----+----------+
| 1 | Mark |
| 2 | John Doe |
+----+----------+
Posts Table
+----+-------------+-------------+
| id | text | author_id |
+----+-------------+-------------+
| 1 | First Post | 1 |
| 2 | Second Post | 2 |
| 3 | Last Post | 1 |
+----+-------------+-------------+
This is just a sample case of use, not real data.
NOTE: I know how to check if user is author on one post, but not multiple in the same row. So basicly that is what I need help with, I guess it must be a left join.
For making the check for the user named Mark and check if he is author for post id 1 I do the following:
SELECT users.*
FROM users
INNER JOIN posts
ON users.id = posts.author_id
WHERE
users.name = 'Mark'
&&
posts.author_id` = 1
I just selected the id from users. If you need more columns then just add it to the select and the group by clause.
SELECT users.id
FROM users
INNER JOIN posts ON users.id = posts.author_id
WHERE users.name = 'Mark'
AND posts.author_id in (1,3)
GROUP BY users.id
HAVING count(distinct posts.author_id) = 2
Use a sub-query to find only users with both 1 and 3:
SELECT users.*
FROM users
WHERE users.name = 'Mark'
and 2 = (select count(distinct posts.id)
where users.id = posts.author_id
and posts.id IN (1,3))
SELECT users.name, posts.posts, posts.authorid
FROM users INNER JOIN posts ON users.id = posts.authorid where posts.authorid = 1
You need to use HAVING clause to achieve desired outcome:
SELECT users.name
FROM users
INNER JOIN posts
on users.id = posts.author_id
WHERE users.name = 'Mark'
GROUP BY users.name
HAVING COUNT(posts.author_id) > 1

Mysql - join 2 queries with limit

Im not very familiar with using 'join' in queries. I really tried solving this by my own, but it seems to be too hard.
I got 2 Tables:
Table 'users':
+-----------------+-----------------+
| member | online |
+-----------------+-----------------+
| mahran | 1 |
| peter | 1 |
| Jen | 1 |
| Steve | 0 |
+-----------------+-----------------+
Table 'tickets'
+-----------------+----------------+----------------+
| name | category | time |
+-----------------+----------------+----------------+
| mahran | silver | 1 |
| peter | blue | 1 |
| mahran | blue | 2 |
| peter | red | 3 |
| peter | green | 2 |
| Jen | silver | 1 |
+-----------------+----------------+----------------+
The chellange:
I need each member (users.member) who's online (users.online). The next thing is to get the category for each member (user.member = tickets.name) with the highest time (probably ORDER BY time DESC LIMIT 1).
So, for example:
Peter is online. Peters highest time is 3 at the position of category=red. So I want peter to show up in the result with his category 'red'. Mahran would show up with blue. Jen would get silver. And steve would be left out because he's not online.
I hope this was clear. In general I know how the queries would look like but theres no chance for me merging them together.
What needs to be merged:
SELECT member FROM users WHERE online = 1;
|
v for each member
SELECT category FROM tickets WHERE name=users.member ORDER BY time DESC.
So, any ideas how to solve this?
Here is a fiddle with a not working query: Click
You can do this easily with a correlated subquery:
select u.member,
(select t.category
from tickets t
where t.name = u.member
order by t.time desc
limit 1
) as MostRecentCategory
from users u
where u.online = 1;
This can make use of the following indexes: users(online, member) and ticket(member, time, category).
Here is the query you're looking for:
SELECT U.member
,T.category
FROM users U
INNER JOIN tickets T ON T.name = U.member
INNER JOIN (SELECT T2.name
,MAX(T2.time) AS [maxTime]
FROM tickets T2
GROUP BY T2.name) AS M ON M.name = T.name
AND M.maxTime = T.time
WHERE U.online = 1
The use of [name] to join the two tables is not a good practice, it's much better to use keys instead. But my query is just here to help you understanding the process of jointure.
Hope this will help you.
If i understand you correctly
SELECT DISTINCT users.member, tickets.category FROM tickets JOIN users ON users.member = tickets.name WHERE users.online = 1 ORDER BY tickets.time DESC
Can you make sql fiddle?
USE DISTINCT
stackoverflow.com/questions/11704012/mysql-distinct-join
try this
SELECT DISTINCT User.member,Ticket.category FROM users AS USER
INNER JOIN tickets AS Ticket ON (User.member = Ticket.name)
WHERE User.online = 1;
Sorry, but peter seems to be RED, It's time is 3. Don't you?
Depending on table definition, is not guaranteed to have one only result for each user.
For example, if peter has time 3 in two categories, you can get one different category depending of the SQL sorting method.
To be sure, tickets.Category and tickets.time must be in a unique key (both toghether, not a unike key for each field)
Assuming that, the Query could be this.
select t2.name, t2.category
from
tickets t2
INNER JOIN (Select
u.member, max(time)
from users u, tickets t
where
u.member = t.name
and u.online = 1
group by u.member
) as usermaxtime on t2.name = usermaxtime.member;

SQL inner join of multiple tables and search through database

I'm making a search function in PHP and I have three tables that I wish to join to a single one; the three tables looks as follow:
band
ID | bands
---+----------
1 | Muse
2 | Coldplay
3 | etc.
release
ID | releases
---+----------
1 | Showbiz
2 | Origin of Symmentry
3 | etc.
track
ID | tracks
---+-----------
1 | Sunburn
2 | Muscle Museum
3 | etc.
I want these tables to be put into this:
discografic
ID | band_id | release_id | track_id
---+----------+-------------+---------
1 | 1 | 1 | 1
2 | 1 | 1 | 2
3 | etc.
So that the table with the SQL code looks like this:
discografic
ID | bands | releases | tracks
---+----------+-------------+---------
1 | Muse | Showbiz | Sunburn
2 | Muse | Showbiz | Muscle Museum
3 | etc.
I want to INNER JOIN these tables. I joined one but I can't really figure out how the get the last joined as well.
SELECT *
FROM band
INNER JOIN discografic
ON band.id = discografic.band_id
This should probably have its own question; I also want to be able to search this database, but only have the result show up once, and also reference to the band every time. For example, if I search "Showbiz" it will give me "Muse", and only show it once.
Note: This is for testing purposes only, security is none of my concerns.
Try with this query:
select d.id,b.bands,r.releases,t.tracks from discografic as d INNER JOIN band as b on
d.band_id=b.id INNER JOIN release as r on d.release_id=r.id INNER JOIN track as t on
d.track_id=t.id GROUP BY d.id
Try This query
Select a.ID,b.bands,c.releases,d.tracks from discografic as a
inner join band as b on a.band_id = b.ID
inner join release as c on a.release_id = c.ID
inner join track as d on a.track_id = d.ID
where b.bands = 'Muse'
Use this query to insert the data like you wanted:
Insert into discograpy
(id,bands,releases,tracks)
SELECT band.ID,bands,releases,tracks
FROM band
INNER JOIN releases
ON band.id = releases.id
inner join track
on band.id = track.id
Use this query to show you only one band:
Declare #releases varchar(50)
Set #releases = 'showbiz'
SElect distinct bands from discograpy where releases = #releases
Here any variable can be passed or set in place of showbiz. This is an example

Properly building my MySQL JOIN query

I have 2 tables from which I'm trying to pull data from together in 1 query.
Guilds:
id (int) | guild (varchar)
Challenges:
id (int) | challenger (int) | challengee (int)
The "challenger" and "challengee" fields reference a "id" from the Guilds table. Using one query, I'd like to pull the Guild field for both the "challenger" and "challengee" (based on the "guild id"), but am stuck on the correct syntax to use.
SELECT challenges.`id` AS c_id, challenges.`challengee` AS c_challengee, challenges.`challenger` AS c_challenger, guilds.`guild`
FROM challenges
LEFT JOIN guilds
ON challenges.`challengee` = guilds.`id`
Is it possible building a query that would grab both the "challenger" and "challengee" Guild (field)?
An example of the result I'm trying to achieve:
challenge_id | challenger | challenger_guild | challengee | challengee_guild
------------- ------------- ------------------ -------------- -----------------
2 | 8 | oBsolete | 5 | Plague
try
SELECT Guilds.id
, Guilds.guild
, chas.challenger
, chal.challengee
FROM Guilds
LEFT OUTER
JOIN Challenges as chal
ON chal.challengee = Guilds.id as xxx_challengee
LEFT OUTER
JOIN Challenges as chas
ON chas.challenger = Guilds.id as xxx_challenger
ORDER
BY Guilds.id
not sure if it will work
Here you go:
SELECT t.id, t.challenger, t.g_challenger, t.challengee, g2.guild as g_challengee
FROM (
SELECT c1.id, c1.challenger, g1.guild as g_challenger, c1.challengee
FROM Guilds g1
JOIN Challenges c1
ON g1.id = c1.challenger
) t
JOIN Guilds g2
ON g2.id = t.challengee
Working Fiddle: http://sqlfiddle.com/#!2/e036d0/18

Querying 6 tables unable to echo info from master table

I am trying to query 6 separate tables in my mysql database, the structures are as follows;
item
itemitemid | item | description | brand | date | time | path |
actor
actoractorid | name | actorthumb | bio |
brand
brandbrandid | brandname | description | image |
movie
moviemovieid | title | genre | year | moviethumb | synopsis|
users
userid | name | surname | email | password |
request
requestid | userid | itemid | brandid | movieid | actorid | content | requestdate |
Using the following query I can display for example where the requestid=1 I can see the movie in the request, the actor in the movie, the item of clothing they were wearing and its brand.
$requestid = mysql_real_escape_string($_GET['requestid']);
$query = "select r.requestid, m.*, a.*, i.*, b.*
FROM request r INNER JOIN movie m ON m.movieid = r.movieid
INNER JOIN actor a ON a.actorid = r.actorid
INNER JOIN item i ON i.itemid = r.itemid
INNER JOIN brand b ON b.brandid = r.brandid
WHERE r.requestid = $requestid";
However when I try to echo out "content" and "request date" from the request table. The data simply doesn't appear. I also cannot get the info from the user table, e.g the user logging the request by by adding the following join;
$query = "select r.requestid, m., a., i., b., u.*
INNER JOIN users u ON u.userid = r.userid
Please advise?
You aren't SELECTing those fields. Right now, you're only SELECTing r.requestid from the requests table. You need to add references to every field you want to echo.
As far as the User join, you just seem to be joining on the wrong field. You need to join on u.userid = r.userid. Right now, you're joining on u.itemid which doesn't exist. You'll also need to change your SELECT statement to report the fields you want (e.g. SELECT ... , u.name, u.email).
As an aside, you should avoid SELECTing table.* where possible. This can break things when you add a field to a table but don't account for that when processing the results of a query. You should try to be explicit as possible, and SELECT only the fields you want to use - e.g. SELECT users.name, users.email rather than doing SELECT users.*.

Categories