Hallo,
I have sort of daily random image system build with Codeigniter and Doctrine.
There is key function, which returns daily image or sets new one. I am using recursive calling, but sometimes are two images selected for one day.
class Daily extends Controller {
function find_daily(){
$connection = Doctrine_Manager::connection();
$connection->setCharset('utf8');
$q = Doctrine_Query::create()
->select('COUNT(id) as dailycount')
->from('Image')
->where('daily_date = ?',date("Y-m-d"));
$set = $q->execute();
if( $set[0]->dailycount != 0 ){ //theres one or more for today
$q = Doctrine_Query::create()
->select('id')
->from('Image')
->where('daily_date = ?',date("Y-m-d"))
->limit(1);
$set = $q->execute();
$i = Doctrine::getTable('Image')->findOneById($set[0]["id"]);
return $i;
}else { // none image selected with today date
$q = Doctrine_Query::create()
->select('id')
->from('Image')
->where('daily_date = ?', "0000-00-00")
->andWhere("dir =?","queue")
->orderBy("rand()")
->limit(1);
$set = $q->execute();
$i = Doctrine::getTable('Image')->findOneById($set[0]["id"]);
$i->daily_date = date("Y-m-d");
$i->dir = "archive";
$i->save();
$this -> find_daily();
};
}
...
I think theres some stupid mistake.. maybe calling something like “Doctrine::doAllAndClear” before $this -> find_daily(); or I forget something…
Or is better not to use recursive calls?
Thanks.
Well, I’ve not tested this at all, but I’d probably be more likely to write your code more like this; hopefully this will give you some ideas:
However, you need to think about what happens if two people hit your script at the same time. In that case, they may both fetch a new image for the day (because they could each run the first
SELECTat the same time, and both get a result that says there’s no daily image yet.)To avoid that, I’d guess you’d want to wrap this in a transaction, and set a transaction isolation level such that the first caller of the function “wins” the race condition, and subsequent callers block until the update is safely saved. Not entirely sure how you’d do that with Doctrine, but it shouldn’t be hard.
Checking the manual, a
beginTransaction()at the start, acommit()at the end, and a setup before everything else involving$tx = $conn->transaction; $tx->setIsolation('SERIALIZABLE');(otherwise two users could probably still run the SELECT at once) would probably be all you need, but you might want to wait for someone who really knows Doctrine to comment on that…I also saved “today”‘s date at the start of the script, otherwise there’s a danger of it being different during the SELECT and the later UPDATE, if the script runs across midnight.