I am trying to create a simple Support Request system which users can insert their email address in a form though jQuery - Ajax and PHP into MySQL database.
After that I need to send a Confirmation Email to the inserted email owner "
every time that a new request inserted into the database". I was thinking about using the Ajax call from database but I am not sure how to select
1- latest inserted row AND
2- Not selected rows to do this( there might be a situation to have two insert at exact same time then the
SELECT email FROM tbl-request ORDER BY id DESC LIMIT 1;
might return Only the last inserted however there where at least two new entries)?
can you please let me know if there is solution to do this through MySQL Trigger or jQuery Ajax
suffii you can add a new colum to the table eg. status which contain 0 as a default value.
Now every time you send a email then update this value to 1.
so you can select the rows for which an email is not sent yet like this..
SELECT email FROM tbl-request where status=0;
It will select only the latest entry containing status = 0.
There can be many way But as my point of view this also can be a better and simplest way
you can do this using cron job.
Run a cron job line every 5 mins and set a flag to check if mail is sent or not. after sending mail set the flag to 1.
We can easily save the last time we checked in a database or file. This method of doing it would allow you to have the emailer system separate from how the record is inserted, which is what I gather you want given that you're suggesting use of Triggers or AJAX to handle it. This method will work even without access to write the database from the PHP script.
At the end of the email script run:
$fh=#fopen('last-email','w');
if (!empty($fh)){
fwrite($fh,time());
fclose($fh);
}
At the start run
$last_email_time=file_get_contents('last-email');
Then add a timestamp field to your table; this will automatically append the time the record was last edited or added.
Then your query will be:
$last_time_as_sql_date=date('Y-m-d H:i:s', $last_email_time);
$query="SELECT email FROM tbl-request WHERE timestamp>'$last_time_as_sql_date' ORDER BY timestamp DESC LIMIT 1;"
How you actually run the script depends more on your implementation; if on a server back end you could run every 5 minutes using crontab -e
*/5 * * * * php /path/to/script.php
You could send the mail from PHP at the moment the request is inserted, but you may want to keep those processes separated.
To do so, an easy fix would be to add a field 'ConfirmationMailed' or something to indicate that that mail was sent. That way you can just query for requests that weren't emailed yet.
A little bit more flexible would be to create a separate table tblRequestCommunication in which you store the communications about the request.
That table could have:
Id (PK), Date, RequestId
Subject
Content
CommunicationType
The communication type could be an enum or a reference to a separate type table in which you store the types of communication to send. One of those types could be 'Automated confirmation message', and in the table you can even store the exact date time, subject and content of that message.
Now, in your query, all you have to do is search for requests without such a confirmation:
SELECT r.email
FROM
tbl-request r
WHERE NOT EXISTS
( SELECT 'x' FROM tblRequestCommunication c
WHERE c.RequestId = r.RequestId
AND c.CommunicationTypeId = 1 /* Automated confirmation */)
This structure will allow you to expand this system for other types as well, for instance an automated mail when the request was closed:
SELECT r.email
FROM
tbl-request r
WHERE
r.State = 'Closed'
AND NOT EXISTS
( SELECT 'x' FROM tblRequestCommunication c
WHERE c.RequestId = r.RequestId
AND c.CommunicationTypeId = 14 /* Close notification */)
Also, you can store 'manual' e-mails and phone reports that are linked to the request in the same table, so you've got a full history of communication.
So, it's a bit of work to create one or two extra tables and change the query, but the abilities of your system will be a lot larger.
Related
This code works but the problem is that if several people use it simultaneously it will cause problems in the sense that some people wont be registered. So I need to rewrite this in a way that all queries per person are executed and finished before the queries of the next person start.
First, the code reads from the database in order to get to the string of all the people that are registered so far.
$sql_s = $con -> query("select * from schedule where date='$date'");
$row_schedule = $sql_s->fetch_array(MYSQLI_BOTH);
$participants = $row_schedule['participants'];
$participants is a string that looks something like "'Sara':'Richard':'Greg'"
Now the current user (Fredrik) wants to add its name to the string like this
$current_user='Fredrik';
$participants_new=add_participant($participants,$current_user);
add_participant is a php function that adds 'Fredrik' to the participant string. Then I want to replace the old participant string with the new one in the SQL database like this
$sql = $con->query("UPDATE schedule SET participants='{$participants_new}' where date='{$date}'");
The specific problem is that if another person (Linda) reads the database before Fredrik executes
$sql = $con->query("UPDATE schedule SET participants='{$participants_new}' where date='{$date}'");
Linda won't get a string that includes Fredrik, she will get "'Sara':'Richard':'Greg'". And when she has added her name it will look like "'Sara':'Richard':'Greg':'Linda'" and when she updates the database like this
$sql = $con->query("UPDATE schedule SET participants='{$participants_new}' where date='{$date}'");
The string including Fredrik ("'Sara':'Richard':'Greg':'Fredrik'") will be overwritten with ("'Sara':'Richard':'Greg':'Linda'") and noone will ever know that Fredrik registered in the class.
Thus, how can I rewrite this code such that all Fredrik's queries are executed before Linda's queries start?
Your question is very good example, showing why one should always learn database design basics and always follow them.
A separator-delimited string in a database is a deadly sin. For many reasons, but we are interesting in this particular case.
Had you designed your database properly, adding participants into separate rows, there would be not a single problem.
So, just change your design by adding a table with participants, and there will be not a single problem adding or removing any number of them.
Here is an approach to do it :
Theoritically Explanation :
Something like this could work.That everytime when user executes the query so it should check for time the request was made to update the query so.Now there must be time difference between user requests for updation query.
Note : Still It's not guaranteed that it will work as because when you will be having internet problems and the user who submitted the request at first but having internet problems and that's why his update query execution is delayed during that time and the other user comes and he sent request late for the updation query but he was having no internet connection problem so his query will be updated before and I think hence that way first user query will get failed..!
Here is the Code :
<?php
// You need to add another column for saving time of last query execution
$current_time=time();
$current_date=date("Y-m-d",$t);
$query_execution_new_time = $current_time.":".$current_date;
if (empty($row_schedule['query_execution_time'])) {
$sql = $con->query("UPDATE schedule SET query_execution_time='{$query_execution_new_time}' where date='{$date}'");
} else {
$query_execution_time = explode(":",$row_schedule['query_execution_time']);
if ($query_execution_time[0] < $current_time) {
$con->query("UPDATE schedule SET participants='{$participants_new}' where date='{$date}'");
$sql = $con->query("UPDATE schedule SET query_execution_time='{$query_execution_new_time}' where date='{$date}'");
}
}
?>
Try this
No need to fetch first all participants and then update.
only update new participant user.
you can concat result of previous one result saved in database column field.
update schedule
set participants = case when participants is null or participants =''
then CONCAT(participants,'Fredrik') // assume Fredrik a new participant
else CONCAT(participants,':','Fredrik')
end
where date='$date';
That way even if you have multiple participants came at the same time the queries won't run at exactly the same time and so you'll get the correct user at the end.
you don't need to worry about multiple users clicking on them unless you've got millions of users
I am curious what path I should take to accomplish the following. I want multiple computers at one location to be able to view and make changes to data inside a mysql DB with a web browser. I dont have extensive knowledge in this area, but from what I do remember this was very difficult if not impossible.
Example: Lets say I have a record for John and I want 2 computers to be able to edit Johns record. Please note that the computers will not be editing the same portion of Johns record. Lets say one record is changing a status from need to be called to called and the other computer is changing the status of need to be ordered to ordered.
I want a solution that could natively handle this.
My current knowledge is building web interfaces with PHP and SQL. I would like to use these languages as I have some prior knowledge.
So my question: Is this possible? If, so exactly how would it work(flow of info)?
There are several ways that you can accomplish this. There's already some great PHP database editing software packages out there (phpMyAdmin).
To handle this in code though you can either use Transactions (depending on what flavor of SQL you're using this would be done differently)
One of the easier ways to ensure that you don't have people's data clashing with one another is just by adding additional where clauses to your statement.
Lets say you have a user record and you want to update the last name from Smith to Bill, and the user ID is 4.
Instead of writing
UPDATE users SET lastName='Bill' WHERE id='4'
You would add in:
UPDATE users SET lastName='Bill' WHERE id='4' AND lastName='Smith'
That way if someone else updates the last name field while you're working on it, your query will fail and you'll have to re-enter the data, thus faking a transaction
Use Transactions. Updating a single record at the exact same time isn't really supported, but applying one transaction followed immediately by another certainly is. This is native to MySQL.
START TRANSACTION;
SELECT #A:=SUM(salary) FROM table1 WHERE type=1;
UPDATE table2 SET summary=#A WHERE type=1;
COMMIT;
One other thing to do is the old desktop approach. Wich is almost mannualy control the flow of modifications. I will show:
Say that you have a client table with the fields id, firstname, lastname, age. In order to control multiple users updates you will add the version integer default 0 field to this table.
When you populate the object on the form to an user you will also store the actual version that the user has selected.
So lets assume that your client table is like this:
id firstname lastname age version
1 Tomas Luv 20 0
2 Lucas Duh 22 0
3 Christian Bah 30 0
When the user select the client with the id=1 the version of this row is, in this moment, 0. Then the user update the lastname of this client to Bob and submit it.
Here comes the magic:
Create a trigger (before update) that will check the current version of that registry with the version that the user previously selected, something like this (this is just pseudo code, as I'm doing it from my head):
create trigger check_client_version on client before update as
begin
if new.version != old.version then
throw some error saying that a modification already was done;
else
new.version = old.version + 1;
end if;
end;
On the application you check if the update has this error and inform to user that someone else made change on the registry he try to change.
So with the given example it would be like:
1 - The user A selected the row 1 and start editing it
2 - At the same time the user B selected the row 1 and save it before the user A
3 - The user A try to save his modifications and get the error from the application
On this context the user A has the version field pointed to 0 also is the user B but when the user B save the registry it now is 1 and when the user A try to save it it will fail because of the check trigger.
The problem with this approch is that you will have to have a before update trigger to every table in your model or at least the one you are concerned with.
I have a MySQL database that has around 600,000 records in the largest table. The other tables are fairly small in comparison. The data is somewhat normalized but there is some duplication because I'm using it for personal use and when I tried fully normalizing it, I found the queries to be unnecessarily complex and slow from all of the joins. I am using PHP to execute the queries.
Now, for example, say that the 600,000 record table contains email addresses. Imagine I have about 10 applications/clients that need to retrieve an email address from this table based on conditions and joins and no two clients should get the same email address. So, I created a query that selects an email address and then another query that uses the selected email address to update a flag field to mark the email address as "in use" and so another client cannot take the same email address. The problem is the query to select the email address takes about 25 seconds to execute and when two clients execute at the same time, they receive the same email address. The speed is not an issue because the clients will only be executing this query once every few hours but I need the clients to get unique email addresses.
I'm kind of new to MySQL so I don't know if selecting the field and then setting a flag is the proper way to go about this. Is there a way to set the flag before I select the field? Also, I don't know much about transactions but could this be solved using them?
Thanks!
START TRANSACTION;
SELECT email FROM myemails WHERE flag = 0 LIMIT 1 FOR UPDATE;
UPDATE myemails SET flag = 1 WHERE email = '$email';
COMMIT;
Another possible approach is to generate a unique flag in php and update first i.e.
$flag = uniqid();
UPDATE myemails SET flag = '$flag' WHERE flag IS NULL LIMIT 1;
SELECT email FROM myemails WHERE flag = '$flag';
I'm designing a chat application using AJAX, PHP, MySQL and am having a problem.
MySQL table is like this username[varchar11]-taken[enum-0,1](coloumns) generally a user who wants to talk to userX, firstly checks if taken value of userx is 0 or 1
SELECT * FROM usertable WHERE username=userX
If it is 0 the user updates taken as 1:
UPDATE usertable SET taken=1 WHERE username=userX
and starts to talk to userX.
But when there are two users who want to talk to userX and check the taken value at the same time unconsciously, they both get 0 response. As a result of it, both of their updates are taken as 1 afterwards and start to talk to userX at the same time. Effectively, 3 users will be talking each other. This chat site is supposed to allow a user to talk to maximum one user at the same time. I'm trying to design it like that.
I'd appreciate any suggestions. What I need is something which is going to stop other users from checking and updating taken value until one finishes to check and update taken value.
I have a form that users fill in to post their job advertisements and one of the fields they enter is the number of days to keep job 'open'.
I want to be able to automatically change the status of the job to 'closed' after the days that they have entered have elapsed.
The job table is already set up with status and expiry, I just need to know how to automatically change the status from 'open' to 'closed'.
I have little knowledge of MySQL and the queries that I would need to run.
Suggest that you don't keep status as a field in the database, but rather derive that status from the expiry date in your business or presentation tier. This could be done in your SQL code or in client code(PHP).
Some pseudocode:
INSERT INTO JobAd (Description, ExpiryDate)
VALUES (#Description, #Today + DaysToLive)
...
SELECT *
, CASE WHEN ExpiryDate<#today
THEN 'Expired'
ELSE 'Good to go'
END AS JobStatus
FROM JobAd ORDER BY JobStatus, ExpiryDate
When you pull those jobs out, use PHP to write the status back to the user, based partially on ExpiryDate. Again, this could be done in SQL or PHP.
Having the status derived means you won't have to create a maintenance process/task to go update an attribute in the database.