Dynamically fill Quiz Form from Database (PHP/MySQL) - php

I am trying to make a quiz program that will fetch the questions and their choices from a MySQL Database, and display them as a form for the user to fill. The user (upon filling all fields) will then submit. There is no scoring here, the answers are just stored in the database with that user's registration ID (This is because some answers are open, i.e : The user has to physically type in the answer, so this will be evaluated later).
The structure of my tables are (I will post the create query since the table looks distorted and hard to read) :
Table 1 : This contains the q_id (Each question has a unique ID), question (The actual question), quiz_id (The id of the quiz that this question belongs to; There are multiple quizzes!), active (A simple "y" or "n" field that checks if this question is active. If it isnt, the question is not added to the form), and type ('c' for single choice, 'm' for Multiple choice, and 'o' for open/user text input)
CREATE TABLE `db_test`.`insr_questions` (
`q_id` INT(10) NOT NULL AUTO_INCREMENT ,
`question` VARCHAR(255) NOT NULL ,
`quiz_id` MEDIUMINT(8) UNSIGNED NOT NULL ,
`active` CHAR(1) NOT NULL ,
`type` CHAR(1) NOT NULL ,
PRIMARY KEY (`id`) );
Table 2: This contains the fields a_id (which is the unique ID of the answer choice), choice (Which is the actual answer choice), question_id (Which is the question ID that these answer choices belong to), and active (Which is same as above)
CREATE TABLE `db_test`.`insr_answers` (
`a_id` INT(10) NOT NULL AUTO_INCREMENT ,
`choice` VARCHAR(75) NOT NULL ,
`question_id` MEDIUMINT(8) UNSIGNED NOT NULL ,
`active` CHAR(1) NOT NULL ,
PRIMARY KEY (`id`) );
So the idea here is to retrieve a quiz_id and then delve into the questions table and retrieve all the questions that belong to that quiz_id, and then take each question, and retrieve all the answers that belong to that question_id. These will then have to be displayed in a php form.
Any pointers in the right direction are greatly appreciated!
(I am currently reading up on different ways in which this can be done, being a novice in php, so I will add any other code that I work on, to this question!)

Read up on JOINS in MySQL, this way you can get the questions and answers in one go:
SELECT * FROM insr_questions q
LEFT JOIN insr_answers a ON q.q_id = a.question_id
WHERE q.active = 1 AND q.quiz_id = 0
And then use mysql_fetch_array to loop through the results. (notice i used the table aliasses q and a, this reduces typing and used for the quiz_id an arbitrary number)

Finished the problem with the following algorithm :
Begin Loop (i from 0 to count(questions) )
Create TextArea
Textarea.Value = Question[i]
Get count(answers) for Question[i]
Get Type of Answer
Case(c)
Begin Loop (n from 0 to count(answers) )
Create Radio Button[n]
Radiobutton[n].Value = Choice[n]
End Loop
Case(m)
Begin Loop (n from 0 to count(answers) )
Create Checkbox[n]
Checkbox[n].Value = Choice[n]
End Loop
Case(o)
Create TextArea
End Case
End Loop

Related

Putting data in a normalized database

I am building a database system wherein users can vote on questions that they themselves can create. What I am struggling with is populating the database correctly, especially because I have tried normalizing it and it confuses me greatly! I am extremely new to SQL and MySQLi.
In the below database, how would I best put in user information when a person makes a profile on my webpage and then use that information again when I, for example, want to tie their UserID to the QuestionID and the Up- or down-votes they made?
I'm looking for assistance on the design of the database, but especially help with my question about putting data into the database and 'tying' it together.
I'd try out something simpler and normalize it one step at a time.
Users table is good
create table users (
userid int not null auto_increment primary key,
email varchar(100) not null,
password varchar(255) not null
);
Questions table
I'd just mark who asked the question right in the questions table to begin with
create table questions (
questionid int not null auto_increment primary key,
question mediumtext or whatever,
userid int not null,
add fk here for userid
);
Question votes - many to many/junction table
create table question_votes (
question_votesid int not null auto_increment primary key,
questionid int not null,
voterid int not null,
votedate timestamp not null default current_timestamp,
score int not null,
add fk here for questionid and voterid
);
Answers table
create table answers (
answerid int not null auto_increment primary key,
answer mediumtext or whatever,
questionid int not null,
userid int not null,
add fk here for questionid, userid
);
You have the option of creating answers that only holds answers and a junction table called question_answers that holds questionid, answerid columns. You can also choose to put question_userid and answer_userid in this table. Read the last paragraph to get to an answer by yourself.
Answer votes table
create table answer_votes (
answer_votesid int not null auto_increment primary key,
answerid int not null,
voterid int not null,
votedate timestamp not null default current_timestamp,
score int not null,
add fk here for answerid and voterid
);
Put some dummy data into it and start asking yourself questions - what kind of information do I need out of this. What if X happens; can this schema adapt to the answers I will be asked in the future when X happens? etc.
As you go through those Q&A in your head, you will find out where you should normalize and to what degree.

What should be the php/MySql query for sending and receiving data from the following database design

this is my database design for a ques n ans page.
A user can ask as many questions, receive as many answers to a single question.
ie. one question and many answers.
All i want to know is mysql query that'll be required for fetching the answers for a particular database. Also the php code that'll be involved in sending and thereby retrieving the results.
db design:
create table user (
name varchar(14) not null.
id int(11) auto_increment;
CREATE TABLE questions (
question_id int(11) NOT NULL AUTO_INCREMENT,
user_id int(11) references user(id),
title varchar(255),
content text,
PRIMARY KEY (question_id);
CREATE TABLE answers (
answer_id int(11) NOT NULL AUTO_INCREMENT,
question_id int(11) REFERENCES questions(question_id),
content text,
PRIMARY KEY (answer_id);
If you already know the ID for the particular question, then all you need to do is select all the records in answers with that question_id. If you're selecting based on some other attribute of the question, then you'd select that question from questions with LEFT JOIN answers on questions.question_id = answers.question_id in order to get all the relevant answers.
As for PHP code, read up on mysqli or PDO. This should be a textbook case of running a query and then fetching the results. Check out example 1 on this page for how to create a DB connection and example 1 on this page for a very simple query example. But note that if you're searching on a user-provided string, a simple query like that creates huge security holes! You should use the prepare() function instead.

How to structure database for unread item alerts per user

I just have a general database theory question. I have a need to make something similar to showing what posts/items a user has viewed or not (such as in a forum) or an unread email message. What I have is there are posts that multiple users can view, but it needs to separate by user who has actually viewed it. So if User A viewed Post 1, it would no longer show that Post 1 is a new item to view, but to User B, it would still show that Post 1 is a new item to view.
I've search for other ideas and one of them is to get a timestamp of when the user last logged in, but I actually need to keep track of the posts they've seen as opposed to posts that have happened since they last logged in.
I would like a MySQL database solution if possible, but I'm open to cookies if that is a must. I could do this on my own and just figure it out, but I'd appreciate any advice on how to properly structure a table(s) to make this the most efficient. Also, bandwidth and storage is not issue.
While reviewing the relevant schema for phpBB, I found the following:
# Table: 'phpbb_topics_track'
CREATE TABLE phpbb_topics_track (
user_id mediumint(8) UNSIGNED DEFAULT '0' NOT NULL,
topic_id mediumint(8) UNSIGNED DEFAULT '0' NOT NULL,
forum_id mediumint(8) UNSIGNED DEFAULT '0' NOT NULL,
mark_time int(11) UNSIGNED DEFAULT '0' NOT NULL,
PRIMARY KEY (user_id, topic_id),
KEY topic_id (topic_id),
KEY forum_id (forum_id)
) CHARACTER SET `utf8` COLLATE `utf8_bin`;
And:
# Table: 'phpbb_forums_track'
CREATE TABLE phpbb_forums_track (
user_id mediumint(8) UNSIGNED DEFAULT '0' NOT NULL,
forum_id mediumint(8) UNSIGNED DEFAULT '0' NOT NULL,
mark_time int(11) UNSIGNED DEFAULT '0' NOT NULL,
PRIMARY KEY (user_id, forum_id)
) CHARACTER SET `utf8` COLLATE `utf8_bin`;
Then I look here in their wiki:
This table keeps record for visited topics in order to mark them as
read or unread. We use the mark_time timestamp in conjunction with
last post of topic x's timestamp to know if topic x is read or not.
In order to accurately tell whether a topic is read, one has to also
check phpbb_forums_track.
So essentially they have a lookup table to store the data associated with a user's viewing of a topic (thread), and then check it against the timestamp in the forum view table, to determine whether the topic has been viewed by the user.
Just create a simple cross-reference table (read_posts or something):
user_id|post_id
----------------
2 | 132
53 | 43
....
Make sure that both of these columns are indexed (especially important that the user_id be indexed) and then use a join (or a sub-query) to select unread posts for the logged in user. If you're just trying to show a list of unread posts, for example, you just run:
SELECT * FROM `posts` WHERE `post_id` NOT IN (
SELECT `post_id` FROM `read_posts` WHERE `user_id`='[$USER ID]')
ORDER BY [your ordering clause]
Based on this description I would use a simple table with maybe 3 columns.
User ID
Post ID
Timestamp First Viewed
When a user views a post, add a row to the table. If a row does not exist in the table for a given user/post id combo, then they have not viewed the post.

What is an effecient way to code separate comment areas?

I realize this is somewhat of an abstract question that has several answers, but I am at a loss as to where to begin. I want to have a separate comments area for each of my blog posts. Should I just set up a different table for the comments for each entry manually every time I update the code to include the latest entry?
Create a new table for the comments with a structure similar to (of course you can customize it to your needs):
Comments
id INT NOT NULL auto_increment,
blog_id INT NOT NULL,
author_id INT NOT NULL DEFAULT 0,
comment text NOT NULL,
added_date DATETIME NOT NULL
The author_id is linked to the users table for logged in users, 0 for an anonymous user. Everything else should be self explanatory I hope.
I'm not sure what you're quite getting at... but it sounds like you want to have comments specific to each post. If that's the case, just simply make a field in the comments table for "post_id" or something similar. Then on each post page just use a SELECT statement to grab comments for that particular post_id.
Simply have a database table storing the post ID in one of the fields. Something similar to the following:
CREATE TABLE blog_comments (
id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
author_id INT(10) UNSIGNED NOT NULL DEFAULT '0',
post_id INT(10) UNSIGNED NOT NULL,
comment TEXT NOT NULL,
added_on TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
);
Then you can simply query for post comments like thus:
$pid = 13; // or whatever your post ID is; could be a $_GET value
$sql = "SELECT * FROM comments WHERE post_id = '$pid' ORDER BY added_on DESC";
Of course, be sure to sanitize the above query, as you don't want any one passing what they feel like for the value of $pid. You need to make sure it's a number, and a number only.

What would be the most effective way to insert tags into a table

I have the following tables;
CREATE TABLE IF NOT EXISTS `tags` (
`tag_id` int(11) NOT NULL auto_increment,
`tag_text` varchar(255) NOT NULL,
PRIMARY KEY (`tag_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=9 ;
CREATE TABLE IF NOT EXISTS `users` (
`user_id` int(11) NOT NULL auto_increment,
`user_display_name` varchar(128) default NULL,
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=10 ;
CREATE TABLE IF NOT EXISTS `user_post_tag` (
`upt_id` int(11) NOT NULL auto_increment,
`upt_user_id` int(11) NOT NULL,
`upt_post_id` int(11) NOT NULL,
`upt_tag_id` int(11) NOT NULL,
PRIMARY KEY (`upt_id`),
KEY `upt_user_id` (`upt_user_id`),
KEY `upt_post_id` (`upt_post_id`),
KEY `upt_tag_id` (`upt_tag_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=9 ;
CREATE TABLE IF NOT EXISTS `view_post` (
`post_id` int(11)
,`post_url` varchar(255)
,`post_text` text
,`post_title` varchar(255)
,`post_date` datetime
,`user_id` int(11)
,`user_display_name` varchar(128)
);
The idea is that I would like to use the most effective way to save tags, for a post and users. Simply once I add a post I pass few tags along that post and user. Later I would like to be able to count tabs for each user and post. Something very similar to Stack Overflow.
I suppose that the 'tag_text' should be unique? Is if effective that I run a function each time I submit a new post to go through the 'tags' table to check if a tag already exists, and if yes, return its 'tag_id' so I can insert it into 'user_post_tag' table.
Is this maybe a bad approach to tackle this kind of issue.
All suggestions are welcome.
Yes, what you are doing is the best way to do it. You created an n to m relationship, as a post can have multiple tags and the same tag can be on multiple posts. You do not want to store the tag name for each of the posts, so you store the id.
But, you should -NOT- have this redudancy of storing multiple times the same tag_id for the same user. It will hit hard your server if the users have multiple tags and you have to execute SELECT count(...) for each of these tags. Do you understand what I'm talking about here? Because right now, how would get how many times the user A has the tag B? You'd have to do SELECT count(*) FROM user_post_tag INNER JOIN tags ON (...) WHERE user_id=A and tag_id=B.
My suggestion is to split user_post_tag into two tables:
user_tags, to count how many times the user has this tag, primary key would be user_id and tag_id and you'd have a count field, which you would just update with count=count+1 everytime this user makes a new post with the tag. This way, you can simply do SELECT tag_text, count FROM user_tags INNER JOIN tags ON (...) WHERE user_id=A to select all tags (with number of times used) of a given user. You're using a fully indexed query. You're not asking MySQL to go over the table, look for a bunch of rows and count them, you're telling to MySQL, go this row at this table and at the other table, join them and give it to me, fast!
post_tags, to store the tags a certain post have, primary key would be post_id and tag_id, no additional fields needed.
I suppose that the 'tag_text' should
be unique? Is if effective that I run
a function each time I submit a new
post to go through the 'tags' table to
check if a tag already exists, and if
yes, return its 'tag_id' so I can
insert it into 'user_post_tag' table.
Yes, it should be unique. It's way better to check if a tag exists before inserting and inserting if it doesn't than having redundancy and having to do SELECT ... count(*) to know how much times the tag has been used. It will be much mess less frequent post creation than post selection, so if you have to pick between being query intensive on insertion and selection, certainly pick insertion.
By the way, if you'd like to have a count of how many posts have the same tag, like in stack overflow, you'd need another table, with primary key tag_id, and then, like on user_tags, you increment the count field everytime a post gets a certain tag.
Hmmm, if your tags are all unique, then you don't need tag_id and tag_text in the tags table. Just use tag_text and make it the primary key. Then look at REPLACE INTO (http://dev.mysql.com/doc/refman/5.0/en/replace.html) to handle new tags.
Associating tags with users or posts? user_tags table and post_tags table. no auto-increment values just a compound key with user_id and tag_text or post_id and tag_text. I don't know if you're looking at the user_post_tags table for a performance increase over joining a post_tags table with posts and users. Still, "replace into" should be your friend here too.

Categories