PHP / Laravel - Execute command every minute for ten minutes - php

Apologies if the answer to this is a simple one, but my brain just can't seem to solve this today, I'm hoping someone has had to solve something similar.
So in my database I have various records with timestamps.
In the 10 minutes prior to the timestamp, I'd like to perform an action and store a result in a field.
I'm well aware that this would be easily solved if I just stored these records vertically, as it is I'd like to have the extra fields tenminutes nineminutes etc in the db - I know this is probably bad DB design but ssshhh, just go with it!
Ok, so my (pseudo)code at the minute read a bit like this:
// In a command that is executed every minute using the scheduler....
$current = Carbon::now();
foreach ($things as $thing) {
if ($thing->thingStartTime->diffInMinutes($current) <= -10) {
//Get data
//Update table field `tenminutes`
}
if ($thing->thingStartTime->diffInMinutes($current) <= -9) {
//Get data
//Update table field `nineminutes`
}
}
Can you see how horrible this will become?
I was thinking along the lines of an associative array, loop through it and have a kind of 'tenminutes' => 10 thing going on?
Or is there a funkier way of using carbon I dont know about? Any ideas?
Other info, this is inside a cron job executed every minute! So if thres a way I can use Laravels scheduler to be smart about this, that would be good to know!

You could iterate through the items and dispatch jobs with different delays?
$current = Carbon::now();
foreach ($things as $thing) {
$delayTime = $current - $thing->thingStartTime;
$job = (new SendReminderEmail($user))->delay($delayTime);
$this->dispatch($job);
}
There is more documentation here.

Related

Laravel and running through a batch of results

I'm having a big head ache with Laravel's chunk and each functions for breaking up result sets.
I have a table, that has a column processed with a value of 0. If I run the following code, it goes through all 13002 records.
Record::where(['processed' => 0])->each(function ($record) {
Listing::saveRecord($record);
}, 500);
This code will run through all 13002 records. However, if I add in some code to mark a record as processed, things go horribly pear shaped.
Record::where(['processed' => 0])->each(function ($record) {
$listing_id = Listing::saveRecord($record);
$record->listing_id = $listing_id;
$record->processed = 1;
$record->save();
}, 500);
When this code runs, only 6002 records are processed.
From my understand of things, that on each iteration of of the chunk (each runs through chunk), that it's executing a new statement.
I've come from using Yii2 and I'm mostly happy with the move, except for this hiccup, which has me pulling my hair out. Yii2 has similar functions (each and batch), but they seem to use result sets and pointers, so even if you update the table while you're processing your results, it doesn't effect your result set.
Is there actually a better way to do this in Laravel?
Try this
Records::where('processed',0)->chunk(100, function($records){
foreach($records as $record)
{
// do your stuff...
}
});
https://laravel.com/docs/5.3/queries#chunking-results
Sorry about indentation, on my phone and that doesnt work apperently..

PHP DB caching, without including files

I've been searching for a suitable PHP caching method for MSSQL results.
Most of the examples I can find suggest storing the results in an array, which would then get included to page. This seems great unless a request for the content was made at the same time as it being updated/rebuilt.
I was hoping to find something similar to ASP's application level variables, but far as I'm aware, PHP doesn't offer this functionality?
The problem I'm facing is I need to perform 6 queries on page to populate dropdown boxes. This happens on the vast majority of pages. It's also not an option to combine the queries. The cached data will also need to be rebuilt sporadically, when the system changes. This could be once a day, once a week or a month. Any advice will be greatly received, thanks!
You can use Redis server and phpredis PHP extension to cache results fetched from database:
$redis = new Redis();
$redis->connect('/tmp/redis.sock');
$sql = "SELECT something FROM sometable WHERE condition";
$sql_hash = md5($sql);
$redis_key = "dbcache:${sql_hash}";
$ttl = 3600; // values expire in 1 hour
if ($result = $redis->get($redis_key)) {
$result = json_decode($result, true);
} else {
$result = Db::fetchArray($sql);
$redis->setex($redis_key, $ttl, json_encode($result));
}
(Error checks skipped for clarity)

Php Timer with a Pause Function

I'm trying to figure out a way to create a function to have the user be able to start a project by clicking a button and the timer would start. At the same time, I would like the user to be able to pause as well as stop the timer. When finished with a task, they can then hit 'Finished' to record the total amount of recorded time, minus any paused time, for the task to a variable in which I can record.
I have been researching and found that a Javascript timer wouldn't be very reliable which is why I wanted to go with Php. I figured I could take a Timestamp on 'Start' and 'Stop', but since I'm needing a 'Pause' as well, it's throwing a whole new kink into my plans.
Anyone know of a good way to accomplish this? I know that one Timestamp to another will get the total time, but how can you calculate one to another if the process had been paused?
I don't think "Javascript timer wouldn't be very accurate" is an accurate statement.
However, if you decide to use PHP, you will not be having the script running for the whole duration of the project. What you need is simply a number of markers in an object. You can store the object in a database table or as JSON string in a file. Here's an example using JSON:
[
[1440264185, "start"],
[1440280217, "pause"],
[1440288349, "resume"],
[1440292161, "pause"],
[1440317465, "resume"],
[1440325597, "stop"]
]
The object is built progressively with each event adding the timestamp of the signal received with the signal type (start, pause, resume, stop).
Then, when you want to calculate the amount of time spent working on the project/task you can use code like this:
$markers = json_decode($json_markers, true);
$num_markers = count($markers);
$markers[] = $markers[$num_markers-1];
$time_worked = 0;
for($i = 0; $i < $num_markers; $i++) {
if(in_array($markers[$i][1], array("start","resume"))) {
$time_worked += $markers[$i+1][0] - $markers[$i][0];
}
}
var_dump($time_worked); // int(27976)
Here's the code above.

PHP foreach & Run at certain time

First Part:
I am trying to write a script to email us when a service call is not paid.
Heres' what I have got started:
$query = "SELECT * FROM service";
$result = mysql_query($query);
while($row = mysql_fetch_row($result)){
$id = $row[0];
$dateEntered = $row[1];
$type = $row[2];
$account = $row[3];
$dateCompleted = $row[4];
$notes = $row[5];
$status = $row[6];
foreach($status == 'Unpaid'){
mailBadAction($id, $account, $status);
}
}
I am not sure if I wrote the foreach right (probably didn't, and I'm not in environment where I can just try it because its hooked into everything else.)
But basically, it will load all of the service calls in my while statement. I want to check each records $status, and check if it is 'Unpaid', and if so, run the function mailBadAction() and pass the $id, $account, $status, $dateEntered to the function. I only want this to happen ONCE a day.
Second Part:
I need this to run everyday at a certain time, once a day. I have zero understanding of cron jobs so I think that is out unless someone wants to help me out with that. But what I have learned is if I just include this page on the index or login page, it will run when someone simply hits the login page. But this will run it for every single time someone hits the index page.
Can someone help out?
As you are already in a loop, going though the results, so you don't need a foreach, you just need if:
if($status == 'Unpaid'){
mailBadAction($id, $account, $status);
}
And if you are on a linux environment, you need cron to run something once a day.
The easiest thing to do (if your system has it...), is add the php script to /etc/cron.daily and make it executable. Your script would look something like (depending on the environment...):
#!/usr/local/bin/php
<?php
// your script
?>
And you really should test it...
Jeroen's answer is correct, you need an if statement.
You could get around setting up a cron job by adding a database check to make sure it's been at least 24 hours since it last sent out emails, and updating that timestamp. But that's honestly more trouble than it's worth. It would block and slow down immensely as it processed data and sent emails, and some poor sap would be sitting there wondering why the page is taking forever. Learning how to setup a cron job would be much more valuable.
I think you got the foreach syntax wrong:
foreach ($array as $value) {
//here you can use $value as the current array field
}
But like said before, you can either adjust your query only to give you the fields that are unpaid:
SELECT id,account,status FROM service WHERE status = 'Unpaid'
(i dont know how exactly your table looks like, but i imagine that structure)
Now every result coming from your DB is "Unpaid", so the testing if(status=='Unpaid') is unnecessary.
For the Cronjobs look on google after "cron php" and you may get :
http://www.thegeekstuff.com/2011/07/php-cron-job/
That means you pop the Script completely from the main site and run it as an stand-alone system, without acces from the web.

How to parse repeating events with Zend GData?

there are plenty of tutorials on how to request and parse a list of events from Google Calendar using Zend GData.
But all tutorials assume that events never repeat. (Kind of, they describe how to set up repeating events, but not how to parse / display them.)
So I wrote a script to copy events from Google Calendar to a web site, but it just doesn't work because some of the events in the calendar are repeating and the method described in the tutorials results in pretty random output.
Any idea?
I think I've finally found the answer you're really looking for. Per http://code.google.com/apis/calendar/data/1.0/reference.html#Parameters, you need to set the 'singleevents' parameter to 'true', forcing the data returned to do it's own parsing and ordering of recurring events. So your code (based on http://code.google.com/apis/calendar/data/1.0/developers_guide_php.html#RetrievingDateRange) will look something like:
function outputCalendarByDateRange($client, $startDate='2007-05-01', $endDate='2007-08-01') {
$gdataCal = new Zend_Gdata_Calendar($client);
$query = $gdataCal->newEventQuery();
$query->setUser('default');
$query->setVisibility('private');
$query->setProjection('full');
$query->setOrderby('starttime');
$query->setStartMin($startDate);
$query->setStartMax($endDate);
$query->setsingleevents('true');
$eventFeed = $gdataCal->getCalendarEventFeed($query);
echo "<ul>\n";
foreach ($eventFeed as $event) {
echo "\t<li>" . $event->title->text . " (" . $event->id->text . ")\n";
echo "\t\t<ul>\n";
foreach ($event->when as $when) {
echo "\t\t\t<li>Starts: " . $when->startTime . "</li>\n";
}
echo "\t\t</ul>\n";
echo "\t</li>\n";
}
echo "</ul>\n";
}
The data that's returned from this function has a single event for each instance of your repeating events, ordered correctly among all the rest of the "normal" events. Exceptions to the recurrance rules (single event cancellations, for instance) are correctly reflected, as well.
So I think you can now use that method without any caveats or warnings...it should give you the data you want, in the way you want.
You can probably do it without the second "foreach" loop, since each event should only have one "when" now...replace lines 18-20 with
echo "\t\t\t<li>Starts: " . $event->when->startTime . "</li>\n";
But since Google's example does include that second foreach loop, it's probably safer to leave it in.
Hope it's not too late to help you!
-----Original answer:-----
(included just for the sake of completeness and because I'm still using this basic method to combine events from multiple calendars)
I'm working on this right now myself, using PHP to parse the feed and display some customized XML based on the data. The only solution I have come up with is to retrieve the dates/times of all the events, recurring or not, using:
$eventFeed = $gdataCal->getCalendarEventFeed($query);
foreach ($eventFeed as $event) {
foreach ($event->when as $when) {
$start=strtotime($when->startTime);
$end=strtotime($when->endTime);
}
}
Which works pretty well. The issue is that all the events will be returned "grouped" in order of the next occurances. That is, say it's Monday right now. If you've got a repeating event every Tuesday and another repeating event every Thursday, and you ask it for all events in the next 90 days, the list you'll get will first list every instance of the Tuesday event for the next 90 days, and THEN it will go on to list every instance of the Thursday event. For my purposes (and it sounds like, yours too), I wanted the list to be in order of the individual events coming up.
The only way I've found to do it, is to insert the data from each individual instance into a temporary SQL database table, including a column indicating the timestamp of the event's beginning. Then once it's all entered in the database, I can request that it give me back the events, ordered by the timestamp.
Thus my loop became something like:
mysql_query("CREATE TEMPORARY TABLE `temp` (`title` TEXT NOT NULL,`date` TEXT NOT NULL,`timestamp` TIMESTAMP NOT NULL)");
$eventFeed = $gdataCal->getCalendarEventFeed($query);
foreach ($eventFeed as $event) {
foreach ($event->when as $when) {
$start=strtotime($when->startTime);
$end=strtotime($when->endTime);
mysql_query("INSERT INTO `temp` (`title`,`date`,`timestamp`) VALUES ('".$event->title->text."','".date("M d h:i a",$start)."-".date("h:i",$end)."','".date("Y-m-d H:i:s",$start)."')");
}
}
$result=mysql_query("SELECT * FROM `mobile_app_events` ORDER BY `timestamp` ASC");
while($row=mysql_fetch_assoc($result)) {
echo "<item>\n";
echo "<title>".$row['title']."</title>\n";
echo "<date>".$row['date']."</date>\n";
echo "</item>\n";
}
Now, I'll caution you- the reason I've found this topic is because I'm looking for an answer myself...it seems that if the recurring events have any exceptions (for instance, next Thursday's event is cancelled), that doesn't get reflected in the output using these codes. Though next Thursday's event is deleted from your Google Calendar view, it still shows up on this page.
Other than that, (and assuming you've got access to a database), this seems to do the trick. I did add in a few lines to start a transaction before the process, with the theory that it might speed up the rendering of the data, not having to commit every insert.

Categories