MySQL - Getting Duplicate Rows on Table Join with Relational Table when Outputting - php

I am making a music site on which there will be a CMS to add new songs, and the visitor can see/play the songs on the news feed or browse/search by artist/song. Songs are referred to as tracks from here on.
The database looks as follows:
tracks
id | title | artist | year | path_mp3 | path_ogg | date_time
artists
id | artist (I have this table because I'd like to add additional artist info later on.)
track2artist
track_id | artist_id
Some tracks have two or more artists, with those artists each having their own solo tracks as well. Therefore I'd like to keep these artists as separate entities so that users can browse to them accordingly, yet when applicable, show that multiple artists were responsible for the same song.
When I output a track on the news feed that has multiple artists, I would like to link to a page which grabs only tracks which both artists contributed to, and from that page, if the visitor clicks one of the artists, to go to the individual artist page.
http://www.discogs.com/search?q=cid+inc+and+victor+hugo&type=all
For example, if you click this link and choose the top search result, you can see the effect I'm looking for. The artist links are separate on the landing page.
What I'm getting with my current code is that tracks that have multiple entries in the track2artist table (by having multiple artists), are getting displayed multiple times. I would like each track to occur only once, yet have it still be known if there are multiple artists, and what their ids are.
I have tried including the "group by tracks.id" at the end of the sql statement, which does in fact select each statement only once. However, this fails to pick up any multiple artist_id entries in track2artist, so I have no way of passing these multiple artist ids when the user clicks.
Would I need new SQL syntax or is it in my PHP? I am trying to get away with as few database calls as possible, as I know that's what is recommended.
I appreciate any help on this. This is possibly covered elsewhere, but when searching, I was rather confused when trying to understand other examples. Thanks for taking the time to read this.
$conn = dbConnect('read');
$sql = "SELECT * FROM tracks INNER JOIN track2artist ON tracks.id = track2artist.track_id";
$result = $conn->query($sql) or die (mysqli_error($conn));
?>
<table id="feed">
<?php
while($track = $result->fetch_assoc()) {
?>
<tr>
<td><a href="javascript:;" id="playlist-add-track-id-<?php echo $track['id']; ?>">
<img src="/assets/img/add_track.gif" class="add_track_icon"></a></td>
<td><?php echo $track['title']; ?> by <a href="/?page=artist&artist_id=<?php echo $track['artist_id']; ?>">
<?php echo $track['artist']; ?></a></td>
</tr>
<?php } ?>
</table>

use the DISTINCT keyword, which will not return duplicates when joining.
$sql = "SELECT DISTINCT * FROM tracks INNER JOIN track2artist ON tracks.id = track2artist.track_id";

since you have songs with multiple authors in a different table you are going to have the tracks listed more then once. [once for each piece of data that makes the row different]
what you want returned is something like this
track artist
artist
artist
track artist
artist
track artist
track artist
however mysql will only return FULL ROWS meaning for each piece of data it will return a complete row of information. to keep queries low it is best to group the information after the database has returned the info.
I would say instead of outputting right away, put this as your database parse loop
<?php
while($track = $result->fetch_assoc()) {
$tracklist[$track['id']]['title'] = $track['title'];
$tracklist[$track['id']]['artists'][$track['artist_id']] = $track['artist'];
}
this will crunch your array into the format you are expecting to find, btw, you will also have to iterate over the artists array with in the track array to display all the artists associated with each track.

Related

Selecting multiple fields from <option> and adding them to database

Background:
I am creating a three tier e commerce website which sells books. Within my website I have created a form (only accessible to staff members) which allows the staff member to add a new book to the system (the database).
At the moment I have a table within my database which records the following data:
book_isdn // unique identifier of each book.
book_cat
book_title
book_author
etc..
Along with this, I have created a table for book categories which stores the following:
cat_id
cat_title
I have defined the following rows in the categories table:
cat_id 1 = Business books
cat_id 2 = Computing books
cat_id 3 = Science books
cat_id 4 = History books
etc
The problem:
In the form which allows a staff member to add a new book, I have a list:
<select multiple name="b_category" style = "width:150px" required>
<?php
$get_cats = "select * from categories";
$run_cats = mysqli_query($connect, $get_cats);
while ($row_cats = mysqli_fetch_array($run_cats)) {
$cat_id = $row_cats['cat_id'];
$cat_title = $row_cats['cat_title'];
echo "<option value='$cat_id'> $cat_title </option>";
}
?>
</select>
I want to add a new book to the 'books' table with the corresponding cat_id for the category to which the book belongs to (i.e. business, computing etc.).
However, a book can also be in two categories, i.e. a book can be both in the field of business and computing.
The question:
How can I alter the form so that it selects multiple options from and adds them to the database, along with the cat_id?
For example:
if using the form I complete all other fields and select computing and business from the list, I want it so that upon clicking "Add new book", the form data is sent to the 'books' table where I will be able to see the new book and under the field of book_cat, I will see 1,2.
I am completely stumped. Is there any way to approach this issue? I hope I have explained this well.
Thanks.
Ok, let's start with something you have not asked for.
a) DB design
Please do not store a concatenated id value like 1,2 in book_cat.
That makes lookups and search hard, because you need to fetch & split every single time. That might only work for really small systems.
What you are looking for is a relation table from books to categories.
Name it like this books_to_categories, with book_id and cat_id.
Query: SELECT cat_id FROM books_to_categories WHERE book_id = 2;
Result: array one or more ids, then resolve the cat_id to it's name (cat_title) via the category table.
The keyword here is database normalization.
b) Formular
Ok, you have a drop down list box, where you can do multiple selections.
Now, the values of these selections need to be transfered to the server side.
One trick is to use array syntax, instead of
<select name="b_category" size=4 multiple>
just use
<select name="b_category[]" size=4 multiple>
and on the server-side var_dump($_POST['b_category']); to see the values received. Then simply iterate over the values of the array and make your database entries.

How to deal with multiple categories entry for an article website

I am creating a news web application, and each news has a category field, but this category can be one, or many at times. Which means, that a person may enter politics, world, us tags for just one news article. Now, the problem I have is how to insert this into a database. If I just enter the tags directly to database, as pure text, then I when I have to echo it, I could use explode() to separate them, like
$row['tags] = 'politics, world, us';
foreach(explode($row['tags') as $tag){
echo "<a href='{$tag}'> {$tag} </a> ";
}
Which would echo the tags, and create a hyperlink for each tag, but the problem I have with this is that, if user wants to just see a news with a specific tag, it becomes problem because there is ~no way I could query all the rows, sort out, explode the tags and just show the news feed like that. It is doable, but very cumbersome. So, I would like to ask how to do this. I am certain it involves, having having maybe another table called tags but that is as far as I can go
The proper way to do this is to make a many to many relationship in database. This would require additional table containing id's of categories and articles related to each other
Look at this example of multiple users with multiple roles:
In the same way, you can create a relationship of articles to categories
You can either use a SET to store the data or, which I suggest, add another table for tags and one which stores relations between articles and tags as mentioned by Maciej.
articles table:
id | title | content
tags table:
id | label
articles_tags table:
article_id | tag_id
Consider adding foreign key constraints to the last table, this will make your life easier.

How to seperate results from a mysql select

I apologize for this beginner question but unfortunately it is my level.
I have a fairly simple web page for my work, it is a index.php page that when opened goes out to a DB and retrieves the contents of a certain column. It then places the results in a drop down pick list.
Here is my problem, this column is a list of materials for customers. Some customers have more than 1 different type of material, while others have one.
Therefore my pick list can look like:
Apple /n
Orange; Apple; banana/n
banana;peach /n
orange/n
I am trying to come up with something that when I pull the data from the mysql DB that my php seperates the materials and only provides unique items.
Here is my code for creating picklist:
<p><select size="1" name="material" ID="material" onChange="showfield(value);">
<option value=''><None></option>;
<?php
while ($row = mysql_fetch_array($query))
{
$rowmod = strtr($row['material']," ","_");
echo "<option value='$rowmod'>$row[material]</option>";
}
?>
Here is my mysql select:
$query="select distinct material from TABLE-A order by material";
Update:
I think my Mysql is right, I think I played around with the php strtr and I was able to remove the ; and add lines in, but now I do not know how to make it cycle through and create my
here is the new code:
$row[product]";
}
?>
some output from my $row will have only one product, some will have 2 or more, I wonder if I have to put another while loop after the $rowmod?
I have a feeling I am close, but hoping for some guidance.
First of all, you should make a material table, indexed with an auto_increment id, and use that ID in what you call TABLE-A in a column material_id. Like that you'll have a list of unique material in one table dedicated to it, where you can even add some columns for the details of the material, etc..
Then I am unsure of your needs/use-case, but it looks like you'll need a customer_material table to link a customer with its material(s) so that you know which customer uses which material. It would have an id auto-incremented, as it should always be for any table for better practices, a customer_id and a material_id, with an unique index on the both last columns (customer_id+material_id) to be sure you link one material to one customer only once and not many time each material for the same customer.
Then when you'll need to list the materials for a given customer, just use this query:
select m.id, m.name
from customer_material cm
join material m on cm.material_id = m.id
where cm.customer_id = YOUR_CUSTOMER_ID
If you need to list all materials uniquely, you;ll then need this query:
select m.id, m.name
from material m
order by m.name /* optional, to order by the material name */
And voila. As I am unsure of your use-case the schema of the DB might be a bit different, but I think anyway the main problem in your issue is that the DB is not well architected. Lemme know if I something is unclear here.
You mentioned that different customers have different materials, but that is not reflected in your SQL query because there is no WHERE clause, meaning that you are selecting all unique values from the materials table regardless of any condition. But with that aside, I think that if you change your code slightly you will get some data.
$query="select distinct `material` from `TABLE-A` order by material"
<p><select size="1" name="material" ID="material" onChange="showfield(value);">
<option value=''><None></option>;
<?php
while ($row = mysql_fetch_assoc($query))
{
$rowmod = strtr($row['material']," ","_");
echo "<option value='$rowmod'>$row['material']</option>";
}
?>

How to join database tables using cakePHP?

Now I have been trying to tackle this issue for a few hours now, and I been trying google and the documentation and read it over and over, but I can't seem to understand it.
Let me try explain what im doing, but simplify the project, assume I am making a blog with cakePHP. I got this 3 tables to make it simple we use only the needed fields.
Table: posts
Desc: This table store all our posts
Fields:
postid - Identifier for the post
title - The posts title
Table: sections
Desc: this table contains categories
Fields:
sectionid - Identifier
sectionname - Name
Table: postlinks
Desc: this table store relations between posts and categories.
Fields:
postid – Post ID to determine what post is linked to what section
sectionid – Section ID to determine the section this post belong to
Let me now try explain what I want to achieve with cakePHP by showing an example with regular php and SQL codes:
(Note this code was written up on the go and might not work but it should give you an idea on what I'm trying to do)
<?php
/**
*Block of code to fetch all posts
*/
while ( $rows = mysql_fetch_array($query) )
{
$postid = $rows['postid'];
/** Join postlinks and sections **/
$sql = “SELECT sections.*, postlinks.* FROM postlinks
INNER JOIN sections
ON postlinks.sectionid = sections.sectionid
WHERE postlinks.postid = '$postid'”;
$query = mysql_query($sql);
print “-- TITLE --”;
print “-- CONTENT --”;
print “Posted in “;
/** Loop all categories **/
while ( $rows = mysql_fetch_array($query) )
{
print $rows['sectionname'] . “ “;
}
}
?>
And now I will try explain with words what I am trying to achieve:
When a visitor is browsing the web page, and enter the blog part of the site, I wish to list X latest posts, and this posts can be put in more than one category, and we will store all category links in a separate table, and here is where I am stuffed, I can manage to get the links out but I can't seem to join the tables and loop out the name.
Hope someone can shed some light on this problem I got with either point me in the right direction and/or show me an example code.
As mentioned I have been reading the documentation, and I have attempted searching for the answer but
association is implement as a join in cakephp.
please follow the concept for HABTM i.e "Has And Belongs To Many"
make a declaration in you model association.
declare the association in your cakephp query.
recursive -> -1 or 0 or 1 or 2
to Know more about association please visit this link enter link description here

Joining rows as array from another table for each row

I am coding a movie archive script using codeigniter for myself with PHP. I am getting movie's information from IMDb and adding them to my database. I'm adding links for movies I selected using another table called links.
This is the query that I am using to get the movies from database:
$movies = $this->db->query("SELECT *
FROM movies
ORDER BY ".$order['by']." ".$order['type']."
LIMIT ".$limit.", " . $this->config->item('moviePerPage')
);
and I am fetching it in view file like this:
foreach ($movies->result() as $row) {
echo $row->name;
}
Now links must be shown for each movie with the matched IDs (movies could have more than one link). My links table has these columns: 'id', 'movieid', 'name', 'link'
How can I get links for each movie with single MySQL query? Is is posible to get all links that related to current $row movie and bind them to a single variable as array? with this I can loop it with foreach() and show links for each movie.
btw: movies.movieid and links.movieid columns have the same data.
I think you need mysql's GROUP_CONCAT
Do something like this:-
SELECT
movies.*,
group_concat(links.link ', ') as links
FROM movies
LEFT JOIN links
ON links.movieid = movies.movieid
GROUP BY movies.movieid
You will get a comma separated list of links for every movie.
Which you can extract like this:-
foreach ($movies->result() as $row) {
$linksArray = explode(",",$row->links);
}
Updates
I think this is the only way you can get the results without having multiple result rows for a single movie with multiple links.
Just be careful of the maximum length of characters you can get in the result - by default 1024 characters. Read this
Mysql group_concat_max_length and Group concat max length to know how to override the limit.
And like Dan Grossman has poined out, if you feel the links may contain comma, use a different or uncommon delimiter.
JOIN the two tables, just as your question name implies.
SELECT
movies.*,
links.*
FROM movies
LEFT OUTER JOIN links
ON links.movieid = movies.movieid
...
You will get one row per movie-link pair. If there are no links for a movie, you'll get a row with just the movie information and NULLs in the columns corresponding to the links table.
You can loop over these results and put the links for each movie in an array keyed by the movie, if you'd like.

Categories