My web app is using CakePHP 2.4 and I'm planning to build a "drip" email feature to deliver follow-up emails over time. This is a very common feature in modern websites, but I'm surprised by how little resources I'm able to find.
My question: Are there any established patterns for doing this? I have some ideas for how to do it but I don't want to reinvent the wheel.
(I did a quick search and I came up empty.)
Note: I'm aware of services like MailChimp which will do this, but my "drips" need to be done dynamically because they are linked to user actions which may or may not have already taken place. For example: I want to send an email every 3 days if a user has not completed their profile. Once they do, they should STOP getting the drips.
Right as requested.
Basically its more of a logic/workflow issue than anything else.
In your case you want to warn a user after 3 days and every 3 days thereafter that their profile isn't complete.
So first up you need some way of detecting that their profile isn't complete in this case I'll use a flag field IsComplete 0/1. By default its 0 (not complete). We also want to know a date that the user signed up so we'll use a simple mysql timestamp field populated with a mysql date in the format yyyy-mm-dd H:i:s, We also want to know a date the user was last email notified. For this we'll use another mysql field this time a datetime field in the same format yyyy-mm-dd H:i:s.
So our user db now looks something like this (note mysql syntax isn't correct)
IsComplete TINYINT 1 DEFAULT 0,
SignupDate TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
LastWarnDate DATETIME DEFAULT NULL
The NULL on the last warn date is important!
On your front end user profile edit/update page you'll simply add the following into the save bit
if (PSEUDO_CODE_ALL_FIELDS_ARE_COMPELTED==TRUE) {
"UPDATE tblusers SET IsComplete=1";
}
Now we have that bit out of the way lets look at the basic logic.
A drip mailer simply once a day needs to check your DB for pending actions and then send out notification emails as required. We can achieve this with a basic headless cron task running on the linux crond. You can find examples of running a php script via cron on google pretty much first response is exactly what you want. I'll not get into that but more into the actual cron script.
So now we know when the user signed up for your site, we know if their profile is complete and when we last warned them.
Our cronjob is nice and simple all it has to do is check is profile complete if not then check dates and send email.
$query = "SELECT EmailAddress,SignupDate,LastWarnDate FROM tblusers WHERE IsComplete=0"; //we don't need to bring back the completed users so for speed we look at uncomplete only
if (strlen($query->LastWarnDate) < 5) { // this is a little nasty but will work NULL is in the db field which returns 4 characters on strlen so a quick check that its < 5 means that we don't have a proper date in the field. There's better ways of doing this i'm being brief
// ok no last warn date so check sign up date vs now
if (DATE_DIFF($query->SignupDate,now()) >=3) {
//3 days or longer since signups so do first warn
"update tblusers SET LastWarnDate=NOW()";
mail(); //standard php mail function for sending email you can use swiftmail or another component if you like
} // its been less than 3 days so do nothing no need for an else
} else {
//we have a last warn date so check warn date vs now
if (DATE_DIFF($query->LastWarnDate,now()) >=3) {
//3 days or longer since last warn date so update last warn date to now and email
"update tblusers SET LastWarnDate=NOW()";
mail(); //standard php mail function for sending email you can use swiftmail or another component if you like
} //its been less than 3 days so do nothing no need for an else
}
So thats it very basic logic flow and example not valid php but you should be able to base your final solution on the code pretty easy.
Schedule the cron job to run at midnight every night and you're done.
As discussed with #Dave under his answer, I'm considering tracking the drips in a dedicated table in order to buy myself more flexibility...
Create a drips table with the following fields:
id (INT)
type (INT or ENUM) This maps to a specific type of drip with its corresponding conditions, etc
user_id (INT) foreign key linking to the user who will get the email
user_email* (VARCHAR) Optionally, the user's email address
count (INT) Tracks how many times it been sent for use in limiting the number of drips
last_sent (datetime) Tracks the date of the last time it was sent for use in maintaining the interval
When actions happen that potentially relate to drips, I check this table and add/delete scheduled drips accordingly. For example, when a user completes their profile, I would check this table and delete the corresponding drip. (<--This is a shortcoming of this approach, IMO.) I worry a bit about this part because some of the drips depend on various factors that may be difficult to keep track of throughout the code.
Every night, a cron job runs that loops through the drips and sends the emails that correspond to each type and which match the interval set in the code that corresponds to each. The job increments the count field and possibly also deletes records which have maxed out (maybe in query at the end of the job).
Related
I am trying to get the time difference in seconds in my database between two events. On the database, the users table has different columns but I'm working with the status column. If a user places an order on our website the status column shows "pending" and, if it is confirmed by our agents it then switches to "success". I'm trying to get the time difference (in secs) between when it shows pending and when it shows success.
NB: I'll be glad if anyone can explain the time() function in PHP with an example.
You can use MySQL's unix_timestamp() function. Assuming that your table has two records for the two events, and there is a column called eventTime, then two queries like the following can give you the two values which are the respective number of seconds since Epoch. You can subtract the latter by the former and get the time difference
select unix_timestamp(eventTime) ... where status='pending'
select unix_timestamp(eventTime) ... where status='success'
Update
After re-reading your question, I guess your DB design is that there is only one row for the whole life cycle of the transaction (from pending to success). In this case, if all three parties (the agent who updates the status to pending, the agent who updates the status to success, and the agent who needs to find the time difference between the two events) involved are the same thread, then you can keep the two event time in memory and simply compute the difference.
However, I think it is more likely that the three parties are two or three different threads. In this case, I think you must have some mechanism to pass the knowledge (of the first event time) from one thread to another. This can be done by way of adding a new column called lastUpdateTime, or by adding a new table for the purpose of time tracking.
By the way, if you use the second approach, I think MySQL Trigger may be useful for you (so that whenever the table gets updated, it trigger another command to update the second table which is used solely to keep track of event time of elapsed time). This approach allows you to not changing the original table but just add a new table.
In the website im working on i need to add user points. Every user will have it's own points and maximum amount of points will be 200. And upon registration user gets 100 points. With various tasks user points will be deducted.
But main problem im struggling is how to add points to the user, since every user need to gets 1 point every hour unless he have 200 or more points.
My first thought was to do a cronjob where it will run every hour a script which will check if user is verified and if user have less than 200 points and add 1 point to every user.
But after some reading im thinking of different approach which i don't understand quite exactly. The better approach, less server resource consuming would be to run a function which will check every time when user login how many points he have and add appropriate number of points to him. Problem is i don't know how to set it, how to calculate how many points to add to user if he was offline like 8 hours and how to time it? Or even maybe use ajax with timer?
What would be your suggestion and approach to this ?
Edit: Just to add since you ask, users doesn't see each other points.
When a user does something, check the last time you gave them points. If it was 5 hours ago, give them 5 points. If it was 10 hours ago, give them 10 points. Etc. Implement caching so if a user visits your site 50 times in one hour, you don't have to check against the DB every time.
Anyway, short answer is, do the check when loading the user data, rather than automatically every hour for all users whether they are active or not.
UPDATE users
SET points = MIN(points + 1, 200)
I don't really see the problem with this script running as a cron. Would be more problem if you handled each event as transaction points, since you'd have to run something like:
# Generates a row each hour per uncapped user, which may become a lot
INSERT INTO transcations (points, type, created)
SELECT 1, 'HOURLY_INCOME', NOW()
FROM users
WHERE points < 200
Is it relevant for other users, or official/inofficial statistics to check what their current point is? This is quite relevant, since it won't work fully if it only updates upon login.
user_table
---------------
id | reg_date
1 | 2013-10-10 21:10:15
2 | 2013-10-11 05:56:32
Just look how many hours left after user registration, add 100 points:
SELECT
TIMESTAMPDIFF(HOUR, `reg_date`, NOW())+100 AS `p`
FROM
user_table
WHERE
id = 1
And then check in PHP if result more than 200 just show 200.
Hmm, from mysql 5.1 there is neat feature which is basically mysql cron called MySQL Event Scheduler, and i think ill go with that for now since cron script will be very easy to write, small and not time consuming.
All i need to do is to write
UPDATE users SET points = (points +1) WHERE points<200
And add it to mysql event recurring every hour.
Im currently trying to sort out a system on my website (with limited coding experience) that doesnt have many references out there, it would seem.
I currently have paypal IPN integration (untested), that i then use to update the database to give the user "time" on their account. i also have a "stop/start" function in their account that allows them to stop and start their time, with the idea being when they "start" their time will start to count down. conversely, whilst they are "stopped", their countdown will not count down. i guess its similar to pausing an active subscription, but mine is over a short term period.
mysql_query("UPDATE `table` SET
`user_id`=$user_id,
`timeadded`=$timeadded,
`amountpaid`=$mc_gross,
`token`=$txnId,
`currtime`=TIMESTAMP()");
^ that is the snippet i am using to update the mysql table.
$new_time = date('H:i', strtotime('+$timeadded'));
this is what im currently working with to allow me to calculate an end date WITHOUT the start/stop feature i mentioned above. im unsure as to how to proceed from here to integrate that feature though, as obviously the end date would need to be dynamically changing based around other variables than the currentdate.
Ive also referenced this which i think might lead me along the correct track but ive been unsuccessful as of yet.
//time when start clicked
$first = new DateTime
//time when stop clicked
$second = new DateTime
//when start is clicked
exec(mysql_query("UPDATE `newtable` SET `userid`=$userid, `start`= $first"));
//when stop is clicked
exec(mysql_query("UPDATE `newtable` SET `userid`=$userid, `end`= $second"));
//use array to select all entries from newtable where userid=$userid (can fill this in if wanted).
$elapsed = $first->diff( $second );
$timeleft = (strtotime($timeadded) - strtotime($elapsed));
$updateelapsed = mysql_query("UPDATE `newtable` SET `userid`=$userid, `elapsed`=$elapsed")
$allelapsed = mysql_query("SELECT * FROM newtable WHERE userid=$user_id");
$allelapsedarray = mysql_fetch_array($allelapsed);
$timeelapsed = array_sum($allelapsedarray);
//so now i have my total time spent "started", i should be able to compare that to the amount of time they bought. if its more than that, they are rejected, otherwise can continue as normal.
so thats what i have thus far for the start/stop feature to be included. this was just done on the fly after coming across the second link i gave above, and i feel its a more fruitful way of going forward than the potential method i mentioned in the first few paragraphs.
to the below comment - it wont update my database with regards to the start/stop time, nor the elapsed time column. given those things it wont bar people from accessing those areas that need "time", but i assume that that will fix itself when the above is sorted. this method ive developed also seems long winded so i might be overlooking a far simpler way of coding this semmingly straightforward task. thanks in advance for any tips.
We have an internal PHP web app that's used for scheduling. We often have multiple users trying to schedule appointments for the same time slot, but we have a hard limit on how many appointments we can have per available time.
We've used PHP to verify that there are available slots before booking, but there's still enough time between PHP checking the table and the insert that overbooking can still happen.
I believe the solution is a MySQL trigger that checks the table before the insert. The problem is that I need MySQL to be able to count the number of records that have the same "schedule_id" and "schedule_user_date" as the record about to be inserted (this will be how many appointments already exist for that time slot).
I have to somehow let the trigger know what the maximum time slot is, which is where I'm stuck, since this can change from client to client.
If you have other suggestions other than a MySQL trigger, I'd like to hear about those as well.
I know, the title does not sound good however I will explain it clearly. In mySQL table there is a column named expires. It will hold a date in the future that will be checked against current time of the server every time a user logs in.
So, when that day is passed and the user logs in, he will get a message. There are two user roles, number 2 and number 3. Number 3 will never have an ending period. Number 2 have "trial" accounts.
I know how to set the value for users with role 2 and make the check against time() on logging, but what is the value I should place in column expires on users with role 3 and how can I make the check against time for them when logging? Maybe a time at 2020 does the trick but isn't it kind of silly ?
I would probably just store NULL for the non-expiring accounts. When validating them, first test if the column value is null, in which case you have a type 3, then check if the date has passed, wherein you have an expired type 2.
You can save some programming on the application side if you stored it as a date far far in the future, but a NULL makes more logical sense to me.