I need a table to log certain actions users make in WordPress.
As of now, this is the database schema I have camp up with:
id bigint(20) NOT NULL AUTO_INCREMENT,
uid bigint(20) NOT NULL,
type VARCHAR(256) NOT NULL,
data1 TEXT NOT NULL,
data2 TEXT NOT NULL,
data3 TEXT NOT NULL,
timestamp bigint(20) NOT NULL,
UNIQUE KEY id (id)
Let me clarify:
uid: User ID of the wordpress user
type: Type of action the user made (can be 'comment', 'new_post', 'login', etc)
data1/2/3: additional data (for example, ID of comment or post made)
To display the logs, I would query the database and run through a certain filter to get the text to display for that particular log. So it works something like this:
if( $type == 'comment') {
$comment = get_comment( $data1 );
$user = get_user($uid);
echo "User {$user->name} has made a <a href='{$comment->permalink}'>comment</a>";
}
Is this the most efficient way of doing things? It seems quite fine to me as I do not want to just store HTML in the logs table to be outputted.
However, the problem comes where I want to hide a particular log entry when certain conditions are met. Like, for example, if a comment no longer exists, I want to hide that entry. This would pose some problems with pagination. Any suggestions on how I can overcome this?
Thanks!
EDIT:
myplugin_transactions
id bigint(20) NOT NULL AUTO_INCREMENT,
user_id bigint(20) NOT NULL,
type VARCHAR(256) NOT NULL,
timestamp bigint(20) NOT NULL,
UNIQUE KEY id (id)
myplugin_meta
id bigint(20) NOT NULL AUTO_INCREMENT,
txn_id bigint(20) NOT NULL,
key VARCHAR(256) NOT NULL,
data TEXT NOT NULL,
UNIQUE KEY id (id)
Lets say I want to select * from myplugin_transactions where data1 would usually have had been 'x' and data2 been 'y'. How should I do it in this case?
SELECT * FROM myplugin_transactions LEFT JOIN myplugin_meta ON myplugin_transactions.id = myplugin_meta.txn_id WHERE ( ... ? )
This answer is going to be very generic as it doesn't provide any code, but it's also too long for a comment.
Firstly, you shouldn't be storing additional data in those data1, data2, data3 fields. You're using MySQL, so you've got the power of relational databases. Use them.
You should simply have another table, which has an ID field (the ID of the action), and a data field. That way you can store 0 to as-many-items-as-you-want pieces of metadata. I mean, wordpress already does this with metadata right?
Secondly, if a comment is deleted, do you simply want to delete the action related to it? If so, simply hook into the API. I believe there is a hook for delete_comment: http://codex.wordpress.org/Plugin_API/Action_Reference#Comment.2C_Ping.2C_and_Trackback_Actions
Otherwise if you want to keep the action, you can either add an extra field or piece of metadata called, say, deleted. When a comment is deleted, as above: hook into the delete_comment call and update the action to deleted = true. Then when you run your query on all the actions, exclude the deleted statements, eg ... WHERE deleted = NULL ... etc.
EDIT2:
To answer your select statement, something like this could work:
SELECT * FROM myplugin_transactions
LEFT JOIN myplugin_meta AS data1
ON ( myplugin_transactions.id = data1.txn_id AND data1.key = 'data1' )
LEFT JOIN myplugin_meta AS data2
ON ( myplugin_transactions.id = data2.txn_id AND data2.key = 'data2' )
WHERE data1.data = 'x'
AND data2.data = 'y'
Obviously replacing the data1 and data2 keywords with meaningful descriptions.
Related
I am working on a workbank in Code Igniter where people add, edit and review documents. Document information is stored in a MySQL table somewhat like this:
CREATE TABLE `document` (
`owner` varchar(15) NOT NULL,
`id` smallint(6) NOT NULL AUTO_INCREMENT,
`pdf` varchar(250) NOT NULL,
`title` varchar(250) NOT NULL,
`permission` tinyint(1) NOT NULL DEFAULT '1',
`date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`,`pdf`),
) ENGINE=InnoDB AUTO_INCREMENT=35 DEFAULT CHARSET=utf8
The pdf field is the name of the file. Combined with the id they make up for the primary key of the table.
On the frontend part of the application, users often see and use lists of these documents and I want to attach links to them. Imagine that such a link can be created like this:
<a href='some_controller/some_method?pdf=<?php echo $document->handle; ?>'>Link</a>
Where the handle attribute of a supposed $document object is a unique identifier that method some_method uses to load the document that the user clicked.
What is the most secure database field for this? I considered the id field but its auto_increment seems insecure and would allow people to fiddle with the HTML to get forbidden documents. Should I add another unique column with a random n-digit or string hash?
You can do a lot of things here.
About what you suggest of creating a new column on your table and saving the hashed id of the document there will increase a little bit the 'security', so the probability of randomly type an existing id is lower.
But I don't think thats the proper way to do that. My recomendation here is to create a new table that relates the avaible documents per every user, or a table that relates the methods with the users, depending on your application needs.
For example:
//documents_for_users
document | user
0803161 | 1
0803162 | 1
Once this has been done, on your method call, you have to check for this relation between tables before do anything with the document.
Controller
function some_method(){
$id = $this->input->get('pdf');
$user = $this->session->userdata('userid');
if ($this->my_model->check_permission($id,$user) {
// do your stuff with document then
}
}
Model
function check_permission($id,$user){
$this->db->select('*');
$this->db->from('documents_for_users');
$this->db->where('document', $id);
$this->db->where('user', $user);
$query = $this->db->get();
if ($query->num_rows() > 0){
return true;
}
return false;
}
This way your security would be significantly increased
I want to add a certain condition to every single query that is executed. So a certain column within a table needs to be a certain value. I would use defaultScope, but it applies only to SELECT queries. This certain column of course exist in every table throughout the whole database.
EDIT:
The following table:
CREATE TABLE `table` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(200) NOT NULL,
`assigned_user` int(11) NOT NULL,
PRIMARY KEY (`id`)
);
A certain user having ID 1 shall be able to see, edit etc. records that have the value 1 in assigned_user. Using defaultScope he cant see anything else than those, however he is still able to use other query types of the model on records that do not have assigned_user set to 1. This is exactly what I want to restrict, so there is no way he can access other records, by any URL change or different POST variable.
Thanks for every hint, answer and suggestion :)
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
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.
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.