PHP custom chat, coding a block function - php

I've tried to build a Block function without any luck, my SQL skills are not as good as I hoped they would be in this case.
I have one table called "messages" and one table called "blocks"
Now, there's 1 file syncing everything to the Chat, what I'm trying to do is IF user 1 has blocked user 2 than user 1's messages should never reach user 2 and user 2's message should not reach user 1. In short term, if you block someone you can't speak to him/her and him/her can't speak to you!
"blocks" table:
id bigint(20)
user_id tinyint(20)
block_id tinyint(20)
"messages" table:
id bigint(20)
timestamp datetime
dest_type varchar(255)
dest_id bigint(20)
source_type varchar(255)
source_id bigint(20)
message_type varchar(255)
message text
in "blocks" user_id is the owners id of the block row.
and block_id is the id that the owner wants to block.
IF "messages.source_id = blocks.block_id OR messages.block_id = blocks.user_id"
THAN dont let the message get trough. I understand that asking someone to code this for me is quite rude but I'm asking, can anyone give this a shot?
here's the sync.php file:
http://pastebin.com/8iiSCXGS
Big thanks!

I haven't delved very deeply into your code, but perhaps this will help.
Let's start with a reduced database structure as follows:
CREATE TABLE `blocks` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
`user_id` INT UNSIGNED NOT NULL,
`block_id` INT UNSIGNED NOT NULL );
INSERT INTO `blocks` (`user_id`,`block_id`) VALUES
(1,2),(3,4),(2,1);
CREATE TABLE `messages` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
`author_id` BIGINT NOT NULL,
`message` TEXT NOT NULL );
INSERT INTO `messages` (`author_id`,`message`) VALUES
(1,"Message from user #1, who has a mutual block in place with user #2"),
(2,"Message from user #2, who has a mutual block in place with user #1"),
(3,"Message from user #3, who has blocked user #4"),
(4,"Message from user #4, who has been blocked by user #3"),
(5,"Message from user #5, who takes no part in all this blocking business");
Now let's suppose user $n visits the website (where 1≤$n≤5). To figure out which messages can be displayed, we need to perform a LEFT JOIN of the messages and blocks tables — i.e., we want to consider every row of messages together with any row of blocks that contains relevant information (specifically, where the author of the message has blocked user $n, or has been blocked by user $n). If $n=1, we have the following:
SELECT * FROM `messages`
LEFT JOIN `blocks`
ON (`author_id`=`block_id` AND `user_id`=1)
OR (`author_id`=`user_id` AND `block_id`=1);
Here's the result of that query:
+----+-----------+-----------------------------------------------------------------------+------+---------+----------+
| id | author_id | message | id | user_id | block_id |
+----+-----------+-----------------------------------------------------------------------+------+---------+----------+
| 1 | 1 | Message from user #1, who has a mutual block in place with user #2 | NULL | NULL | NULL |
| 2 | 2 | Message from user #2, who has a mutual block in place with user #1 | 1 | 1 | 2 |
| 2 | 2 | Message from user #2, who has a mutual block in place with user #1 | 3 | 2 | 1 |
| 3 | 3 | Message from user #3, who has blocked user #4 | NULL | NULL | NULL |
| 4 | 4 | Message from user #4, who has been blocked by user #3 | NULL | NULL | NULL |
| 5 | 5 | Message from user #5, who takes no part in all this blocking business | NULL | NULL | NULL |
+----+-----------+-----------------------------------------------------------------------+------+---------+----------+
6 rows in set (0.00 sec)
As you can see, the rows we want are the ones where the last three columns are NULL, which means there is no blocking rule affecting the display of this particular message to this particular user. So to extract these messages, we simply add WHERE block_id IS NULL to the end of the query:
SELECT * FROM `messages`
LEFT JOIN `blocks`
ON (`author_id`=`block_id` AND `user_id`=1)
OR (`author_id`=`user_id` AND `block_id`=1)
WHERE `block_id` IS NULL;
+----+-----------+-----------------------------------------------------------------------+------+---------+----------+
| id | author_id | message | id | user_id | block_id |
+----+-----------+-----------------------------------------------------------------------+------+---------+----------+
| 1 | 1 | Message from user #1, who has a mutual block in place with user #2 | NULL | NULL | NULL |
| 3 | 3 | Message from user #3, who has blocked user #4 | NULL | NULL | NULL |
| 4 | 4 | Message from user #4, who has been blocked by user #3 | NULL | NULL | NULL |
| 5 | 5 | Message from user #5, who takes no part in all this blocking business | NULL | NULL | NULL |
+----+-----------+-----------------------------------------------------------------------+------+---------+----------+
4 rows in set (0.01 sec)
If you substitute different user IDs into this query, you should get the results you're after.

Related

Know the last modified row or id's row in a mysql table

I'm using Mysql 5.5 and by example I have a table like this
+------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+----------------+
| idgroups | int(11) | NO | PRI | NULL | auto_increment |
| group_id | int(11) | YES | | NULL | |
| group_name | varchar(45) | YES | | NULL |
Where some people are allowed to do inserts,update and delete but I want to know which is the last modified row or row's id in a given time
Any ideas?
Thanks in advance
My suggestion would be to create a second table. something like edit_history for recording modifications. You can put triggers on your groups table above that says "Any time a record is inserted, deleted, or updated, create a record in my edit_history table".
A trigger can be created as follows:
CREATE TRIGGER trigger_name
AFTER INSERT
ON table_name FOR EACH ROW
BEGIN
-- For each row inserted
-- do something...
END;
Since your field is auto_increment, you can just select the maximum value of idgroups to get the most recently inserted value:
select max(idgroups) from tbl
to get last modified in general will require additional structure to your table. In particular, if you are deleting, you will need to store what you have most recently deleted somewhere.

How can i update the Records included in another query using SUM and GROUP By in mysql

I am having a mysql table
content_votes_tmp
+------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------------+------+-----+---------+----------------+
| up | int(11) | NO | MUL | 0 | |
| down | int(11) | NO | | 0 | |
| ip | int(10) unsigned | NO | | NULL | |
| content | int(11) | NO | | NULL | |
| datetime | datetime | NO | | NULL | |
| is_updated | tinyint(2) | NO | | 0 | |
| record_num | int(11) | NO | PRI | NULL | auto_increment |
+------------+------------------+------+-----+---------+----------------+
surfers can vote up or vote down on posts i.e. content, a record gets inserted everytime a vote is given same as rating , in the table along with other data like ip , content id
Now i am trying to create cronjob script in php which will SUM(up) and SUM(down) of votes
like this,
mysqli_query($con, "SELECT SUM(up) as up_count, SUM(down) as down_count, content FROM `content_votes_tmp` WHERE is_updated = 0 GROUP by content")
and then by using while loop in php i can update the main table for the specific content id,
but i would like to set the records which are part of SUM to be marked as updated i.e. SET is_updated = 1, so the same values wont get summed again and again.
How can i achieve this ? using mysql query ? and work on same data set as , every second/milisecond the records are getting inserted in the table ,.
i can think of another way of achieving this is by getting all the non-updated records and doing sum in the php and then updating every record.
The simplest way would probably be a temporary table. Create one with the record_num values you want to select from;
CREATE TEMPORARY TABLE temp_table AS
SELECT record_num FROM `content_votes_tmp` WHERE is_updated = 0;
Then do your calculation using the temp table;
SELECT SUM(up) as up_count, SUM(down) as down_count, content
FROM `content_votes_tmp`
WHERE record_num IN (SELECT record_num FROM temp_table)
GROUP by content
Once you've received your result, you can set is_updated on the values you just calculated over;
UPDATE `content_votes_tmp`
SET is_updated = 1
WHERE record_num IN (SELECT record_num FROM temp_table)
If you want to reuse the connection to do the same thing again, you'll need to drop the temporary table before creating it again, but if you just want to do it a single time in a page, it will disappear automatically when the database is disconnected at the end of the page.

SELECT id FROM table WHERE id=$_GET['id'] AND user1=$user OR user2=$user

I'm trying to build a similar facebook style messaging system (conversations).
This is the conversation table.
DESCRIBE conversation;
+----------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+----------------+
| c_id | int(11) | NO | PRI | NULL | auto_increment |
| user_one | int(11) | NO | | NULL | |
| user_two | int(11) | NO | | NULL | |
| ip | varchar(30) | NO | | NULL | |
| time | int(11) | NO | | NULL | |
+----------+-------------+------+-----+---------+----------------+
Now before the user can read a conversation, I need to check if the conversation (c_id) exists, and if the user is the owner of the given conversation id. What is the best possible way to write this query?
Example of what I have, which is not working:
$cid = intval($_GET['cid']);
$conv = $this->db->fetchRow('SELECT c_id FROM `conversation` WHERE
user_one=? OR
user_two=? AND
c_id=?',
array($this->user->id, $this->user->id, $cid));
if ($conv) {
// get the conversation replies etc..
}
I see a couple of problems.
One is that you seem to have overlooked that AND has a higher precedence than OR. So the logic of your condition works as if you had written it this way:
WHERE user_one=? OR (user_two=? AND c_id=?)
Whereas I would guess that you intended the logic to work this way:
WHERE (user_one=? OR user_two=?) AND c_id=?
But if that's how you intended it to work, I wonder why you need to search for the user id's at all, since the condition on c_id=? will select only one row (or zero rows if there's no match), because it's searching for one specific primary key value.

Selecting alternating records from two tables

I have two tables in my database: events_users and events_admins. The two are nearly identical (except for a few fields). Both tables also have a promoted field (1 or 0).
+-----------------+-----------------------+-------------------+-------------------+
| events_users_id | events_users_promoted | events_users_name | events_users_date |
+-----------------+-----------------------+-------------------+-------------------+
| 1 | 0 | Users foo | 2012-11-15 |
| 2 | 1 | Users bar | 2012-11-15 |
| 3 | 0 | Users foobar | 2012-11-14 |
+-----------------+-----------------------+-------------------+-------------------+
+------------------+------------------------+--------------------+--------------------+
| events_admins_id | events_admins_promoted | events_admins_name | events_admins_date |
+------------------+------------------------+--------------------+--------------------+
| 1 | 0 | admins foo | 2012-11-14 |
| 2 | 0 | admins bar | 2012-11-15 |
| 3 | 1 | admins foobar | 2012-11-16 |
+------------------+------------------------+--------------------+--------------------+
I cannot put both types of events in one table for various reasons. However, I do want one result, as follows:
All events are ordered by date, most recent first, however an admin event comes first and always alternates with a user event (alternating every record). Promoted user AND admin events are shown first.
+------------------+-----------------------+-------------------+-------------------+
| a 3 | 1 | admins foobar | 2012-11-16 |
| u 2 | 1 | Users bar | 2012-11-15 |
| a 1 | 0 | admins foo | 2012-11-14 |
| u 3 | 0 | Users foobar | 2012-11-14 |
| a 2 | 0 | admins bar | 2012-11-15 |
| u 1 | 0 | Users foo | 2012-11-15 |
+------------------+-----------------------+-------------------+-------------------+
I was wondering if I could do this with one query, using UNION to merge the two tables (the few missing fields in the one table, I'll mark as null), however, I wouldn't know how to sort them. The only way out I see at the moment is using two different queries with a simple ORDER BY promoted DESC, date ASC, put the results in two arrays and merge them alternating with PHP.
EDIT: It seems I haven't explained my goal well enough, as the two current answers don't exactly solve my problem.
The suggested queries first give me all promoted events from one category, then all promoted events of the other type, the normal events of the one type and at last the remaining events of the other type. However, these need to alternate as well: one user promoted, one admin promoted, one user promoted, one admin promoted.... When I've run out of admin promoted events, I want one admin event, one user event, one admin event...
SELECT * FROM (
SELECT
'u' AS TYPE,
id ,
events_users_id AS events_users_id,
events_users_promoted AS promoted,
events_users_name AS name,
events_users_date AS date
FROM eventusers
UNION ALL
SELECT
'a' AS TYPE,
id,
events_admin_id AS events_admins_id,
events_admin_promoted AS promoted,
events_admin_name AS name,
events_admin_date AS date
FROM eventadmins
) AS tmp
ORDER BY ID, TYPE DESC
Check out this http://www.sqlfiddle.com/#!2/5b9a5/10
One more column added to both the tables.
CREATE TABLE `eventusers` (
`id` INT(10) NOT NULL AUTO_INCREMENT,
`events_users_id` INT(10),
`events_users_promoted` INT(10),
`events_users_name` varchar(50),
`events_users_date` date,
PRIMARY KEY (`id`)
);
CREATE TABLE `eventadmins` (
`id` INT(10) NOT NULL AUTO_INCREMENT,
`events_admin_id` INT(10),
`events_admin_promoted` INT(10),
`events_admin_name` varchar(50),
`events_admin_date` date,
PRIMARY KEY (`id`)
);

Speed Up MySQL (MyISAM) COUNTs with WHERE Clauses

We are implementing a system that analyses books. The system is written in PHP, and for each book loops through the words and analyses each of them, setting certain flags (that translate to database fields) from various regular expressions and other tests.
This results in a matches table, similar to the example below:
+------------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------------------+--------------+------+-----+---------+----------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| regex | varchar(250) | YES | | NULL | |
| description | varchar(250) | NO | | NULL | |
| phonic_description | varchar(255) | NO | | NULL | |
| is_high_frequency | tinyint(1) | NO | | NULL | |
| is_readable | tinyint(1) | NO | | NULL | |
| book_id | bigint(20) | YES | | NULL | |
| matched_regex | varchar(255) | YES | | NULL | |
| [...] | | | | | |
+------------------------+--------------+------+-----+---------+----------------+
Most of the omitted fields are tinyint, either 0 or 1. There are currently 25 fields in the matches table.
There are ~2,000,000 rows in the matches table, the output of analyzing ~500 books.
Currently, there is a "reports" area of the site which queries the matches table like this:
SELECT COUNT(*)
FROM matches
WHERE is_readable = 1
AND other_flag = 0
AND another_flag = 1
However, at present it takes over a minute to fetch the main index report as each query takes about 0.7 seconds. I am caching this at a query level, but it still takes too long for the initial page load.
As I am not very experienced in how to manage datasets such as this, can anyone advise me of a better way to store or query this data? Are there any optimisations I can use with MySQL to improve the performance of these COUNTs, or am I better off using another database or data structure?
We are currently using MySQL with MyISAM tables and a VPS for this, so switching to a new database system altogether isn't out of the question.
You need to use indexes, create them on the columns you do a WHERE on most frequently.
ALTER TABLE `matches` ADD INDEX ( `is_readable` )
etc..
You can also create indexes based on multiple columns, if your doing the same type of query over and over its useful. phpMyAdmin has the index option on the structure page of the table at the bottom.
Add multi index to this table as you are selecting by more than one field. Below index should help a lot. Those type of indexes are very good for boolean / int columns. For indexes with varchar values read more here: http://dev.mysql.com/doc/refman/5.0/en/create-index.html
ALTER TABLE `matches` ADD INDEX ( `is_readable`, `other_flag`, `another_flag` )
One more thing is to check your queries by using EXPLAIN {YOUR WHOLE SQL STATEMENT} to check which index is used by DB. So in this example you should run query:
EXPLAIN ALTER TABLE `matches` ADD INDEX ( `is_readable`, `other_flag`, `another_flag` )
More info on EXPLAIN: http://dev.mysql.com/doc/refman/5.0/en/explain.html

Categories