Počet přečtení článku

Školení, která pořádám

Ve skupinách na Facebooku se nově zobrazuje (možná ještě ne všem) počet přečtení jednotlivých zpráv. Jak něco takového realizovat na svém webu?

Pastva pro roboty

Trivialita, řeknete si: mysql_query("UPDATE article SET reads = reads + 1 WHERE id = $id"). Toto řešení ale má dva problémy:

  1. Nesleduje, kolik lidí si článek přečetlo, ale kolikrát byla navštívená stránka.
  2. Přístup započte všem, i robotům.

Důkladné řešení

V první řadě musíme zamezit vložení do (skrytého) rámu. Toho dosáhneme posláním hlavičky X-Frame-Options: deny.

Dále se potřebujeme bránit proti CSRF. Abychom to mohli udělat, tak potřebujeme uživatele nějak identifikovat, zbytek článku tedy bude počítat s nastavenými proměnnými $_SESSION["user_id"] a $_SESSION["csrf_token"].

Článek se má označit jako přečtený automaticky, takže se u takto jednoduchého problému kupodivu neobejdeme bez JavaScriptu:

<script type="text/javascript">
jQuery.post('read-article.php?id=<?php echo $id; ?>', {
	csrf_token: '<?php echo $_SESSION["csrf_token"]; ?>'
});
</script>

Volání si můžeme ušetřit, pokud při zobrazování zjistíme, že aktuální uživatel už článek dříve četl.

Na straně serveru pak čtenáře uložíme (nad dvojicí cloupců article_id, user_id je unikátní klíč):

<?php
// read-article.php
if ($_SESSION["csrf_token"] == $_POST["csrf_token"]) {
    mysql_query("
        INSERT IGNORE INTO article_reads (article_id, user_id)
        VALUES (" . intval($_GET["id"]) . ", $_SESSION[user_id])
    ");
}
?>

Uložení až po dočtení

Pokud je článek delší, tak ho můžeme chtít za přečtený označit až po nějaké době od focusu okna. Pokud si uživatel otevře odkaz do panelu na pozadí (což osobně dělám často), tak se článek jako přečtený zatím neoznačí:

<script type="text/javascript">
jQuery(window).one('focus', function () {
	setTimeout(function () {
		jQuery.post('read-article.php?id=<?php echo $id; ?>', {
			csrf_token: '<?php echo $_SESSION["csrf_token"]; ?>'
		});
	}, 10000);
});
</script>

Přečtení se dá taky spustit po dojetí na konec článku. K tomu bych nejspíš využil nějaký jQuery plugin.

Jakub Vrána, Řešení problému, 20.8.2012, diskuse: 8 (nové: 0)

Diskuse

Rudolf:

Chybí tady G+ abych mohl dát článku +1 ;-) Takže aspoň v podobě komentáře. Hezky a jasně napsáno, díky, přesně co jsem chtěl za pár dní řešit, takže přišlo i vhod.

starenka OpenID:

Proc X-Frame-Options: Deny? Nestaci Same-Origin?

ikona Jakub Vrána OpenID:

Stačí.

Peter:

A čím je dosiahnuté, že roboti sa nezapočítajú? Len tým, že to ide cez jQuery?

ikona Jakub Vrána OpenID:

I když by robot spustil JavaScript, tak nejspíš nespustí událost focus. I když by ji spustil, tak nejspíš nebude čekat 10 sekund na timeout. A pak taky samozřejmě záleží na tom, kdy a jak naplníme $_SESSION["user_id"].

Vopičák:

A zároveň se nezapočítají uživatelé bez JS...

ikona Michal Gebauer:

Těch je málo a většina, co nemá povolený javascript chce být anonymní.

ikona Petr Jirásek:

Velice vhodné. Právě tohle jsem hledal. +1

Vložit příspěvek

Používejte diakritiku. Vstup se chápe jako čistý text, ale URL budou převedeny na odkazy a PHP kód uzavřený do <?php ?> bude zvýrazněn. Pokud máte dotaz, který nesouvisí s článkem, zkuste raději diskusi o PHP, zde se odpovědi pravděpodobně nedočkáte.

Jméno: URL:

avatar © 2005-2016 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.