[PHP] Tips over code compacter te maken

Pagina: 1
Acties:
  • 120 views sinds 30-01-2008
  • Reageer

Onderwerpen


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Ik ben voor mijn website bezig met een statistiekentooltje. Hier is onder andere een overzicht opgenomen van het aantal hits en sessions van vandaag, gisteren, deze -en vorige week, deze -en vorige maand. Ik gebruik hiervoor de volgende functie:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
function stats_period($timeframe) {
  $sql = 'SELECT date, SUM(hits) AS hits, SUM(sessions) AS visits FROM stats_dt ';
  switch ($timeframe) {
    case 'today':
      $today = date("Y-m-d", mktime(0, 0, 0, date('n'), date('j'), date('Y')));
      $sql.= "WHERE date = '$today'";
    break;
    
    case 'yesterday':
      $yesterday = date("Y-m-d", mktime(0, 0, 0, date('n'), date('j') - 1, date('Y')));
      $sql.= "WHERE date = '$yesterday'";
    break;
  
    case 'this_week':
      $today = date("Y-m-d", mktime(0, 0, 0, date('n'), date('j'), date('Y')));
      $week_start = date("Y-m-d", mktime(0, 0, 0, date('n'), date('j') - date('w'), date('Y')));
      $sql.= "WHERE date BETWEEN '$week_start' AND '$today'";
    break;
    
    case 'prev_week':
      $week_start = date("Y-m-d", mktime(0, 0, 0, date('n'), date('j') - (date('w') + 7), date('Y')));
      $week_end = date("Y-m-d", mktime(0, 0, 0, date('n'), date('j') - (date('w') + 1), date('Y')));
      $sql.= "WHERE date BETWEEN '$week_start' AND '$week_end'";
    break;
    
    case 'this_month':
      $today = date("Y-m-d", mktime(0, 0, 0, date('n'), date('j'), date('Y')));
      $month_start = date("Y-m-d", mktime(0, 0, 0, date('n'), 1, date('Y')));
      $sql.= "WHERE date BETWEEN '$month_start' AND '$today'";
    break;
    
    case 'prev_month':
      $month_start = date("Y-m-d", mktime(0, 0, 0, date('n') - 1, 1, date('Y')));
      $month_end = date("Y-m-d", mktime(0, 0, 0, date('n'), 0, date('Y')));
      $sql.= "WHERE date BETWEEN '$month_start' AND '$month_end'";
    break;
  }
  $sql.= ' GROUP BY date DESC';
  return db_fetch_object(db_query($sql));
}

Ik heb alleen erg het gevoel dat dit veel korter / efficienter kan, al zie ik het niet. Wat vinden jullie ervan? Wie heeft tips hoe dit korter op te schrijven?

Acties:
  • 0 Henk 'm!

  • Cartman!
  • Registratie: April 2000
  • Niet online
http://nl2.php.net/strtotime misschien een idee?

Overigens is korter schrijven van code niet per definitie beter imo, het komt de leesbaarheid niet altijd ten goede. Als je er na een jaar weer naar kijkt snap je er misschien geen hout meer van en probeer dan die bug maar eens te vinden ;)

Acties:
  • 0 Henk 'm!

  • ? ?
  • Registratie: Mei 2007
  • Niet online

? ?

code:
1
stats_period($date_from, $date_till)


een functie moet al die opties niet hebben, gewoon van een datum tot een datum

[ Voor 59% gewijzigd door ? ? op 25-10-2007 14:09 ]


Acties:
  • 0 Henk 'm!

  • Kalentum
  • Registratie: Juni 2004
  • Laatst online: 16:37
je zou je database kunnen gebruiken om de intervallen te bepalen. Dus geen PHP meer maar de database-specifieke functies. BV van Mysql

Acties:
  • 0 Henk 'm!

Verwijderd

Wat ik meestal doe is als iets meer dan 2 keer voorkomt in een stuk code, kun je er beter een functie van maken. In jouw geval kun je dus voor het opbouwen van die where clause een functie maken waar je wat parameters aan mee geeft.

Acties:
  • 0 Henk 'm!

  • Japius
  • Registratie: April 2003
  • Laatst online: 30-08 20:57
Als je Date veld van het type datetime is, kan je ook werken met de date_sub:

code:
1
WHERE date > date_sub(current_date(), interval 1 day)

Acties:
  • 0 Henk 'm!

  • ? ?
  • Registratie: Mei 2007
  • Niet online

? ?

delete

[ Voor 95% gewijzigd door ? ? op 25-10-2007 14:51 ]


Acties:
  • 0 Henk 'm!

  • Erkens
  • Registratie: December 2001
  • Niet online

Erkens

Fotograaf

era.zer schreef op donderdag 25 oktober 2007 @ 14:49:
Zo 'ingewikkeld' allemaal?
Gewoon als INT in mysql. time() geeft je de huidige int waarde en met date kun je de int omzetten naar een menselijke datum

en een INT is zo makkelijk te vergelijken? Gewoon startdatum & stopdatum >= en <=
Je kan beter een timestamp type gebruiken in mysql voor tijden (tenzij je een groter bereik nodig hebt, dan moet je datetime gebruiken). Vergelijken etc kan je dan gewoon doen met de mysql functies wat gewoon veel handiger is.

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
De statistieken van de website worden elke dag (en elk uur van de dag) samengevat in stats_dt:
code:
1
2
3
4
5
6
7
8
9
+------------+------+----------+------+
| date       | hour | sessions | hits |
+------------+------+----------+------+
| 2007-10-24 |   12 | 4        | 53   |
+------------+------+----------+------+
| 2007-10-24 |   13 | 8        | 65   |
+------------+------+----------+------+
| 2007-10-25 |    7 | 2        | 17   |
+------------+------+----------+------+

Het was een goed idee om de MySQL functies te gebruiken voor deze datum / tijd calculaties, ipv eerst te gaan rekenen in PHP :) Ik probeer eea nu om te zetten:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
// vandaag
// date("Y-m-d", mktime(0, 0, 0, date('n'), date('j'), date('Y')))
$sql.= "WHERE date = CURDATE()";

// gisteren
//date("Y-m-d", mktime(0, 0, 0, date('n'), date('j') - 1, date('Y')))
$sql.= "WHERE date = DATE_SUB(CURDATE(), INTERVAL 1 DAY)";

// deze week
// $today = date("Y-m-d", mktime(0, 0, 0, date('n'), date('j'), date('Y')));
// $week_start = date("D Y-m-d", mktime(0, 0, 0, date('n'), date('j') - date('w'), date('Y'))); 
$sql.= "WHERE YEARWEEK(date) = YEARWEEK(CURDATE())";


Maar ik loop vast bij gegevens ophalen van de afgelopen week. Ik gebruikte:
PHP:
1
2
3
$week_start = date("Y-m-d", mktime(0, 0, 0, date('n'), date('j') - (date('w') + 7), date('Y')));
$week_end = date("Y-m-d", mktime(0, 0, 0, date('n'), date('j') - (date('w') + 1), date('Y')));
$sql.= "WHERE date BETWEEN '$week_start' AND '$week_end'";

Nu zou ik voor de afgelopen week YEARWEEK(CURDATE()) - 1 kunnen pakken, maar dan kom ik in problemen in de eerste week van het jaar. Stel dat YEARWEEK(CURDATE()), 200801 oplevert. Wat gebeurt er dan als ik er daar 1 vanaf haal? Zet MySQL dat dan om in 200752?

ik weet trouwens dat "date" een gereserveerd woord in MySQL is, en daarom niet in je tabel als kolomnaam gebruikt mag worden...moet dit nog aanpassen, maar voorlopig werkt het wel :)

Acties:
  • 0 Henk 'm!

  • Erkens
  • Registratie: December 2001
  • Niet online

Erkens

Fotograaf

Verwijderd schreef op donderdag 25 oktober 2007 @ 15:21:
ik weet trouwens dat "date" een gereserveerd woord in MySQL is, en daarom niet in je tabel als kolomnaam gebruikt mag worden...moet dit nog aanpassen, maar voorlopig werkt het wel :)
Dan zet je die naam tussen `backticks` wat je eigenlijk altijd om je kolomnamen moet zetten...

Acties:
  • 0 Henk 'm!

  • Grijze Vos
  • Registratie: December 2002
  • Laatst online: 28-02 22:17
Erkens schreef op donderdag 25 oktober 2007 @ 14:52:
[...]

Je kan beter een timestamp type gebruiken in mysql voor tijden (tenzij je een groter bereik nodig hebt, dan moet je datetime gebruiken).
Euhm, volgens mij niet. Timestamps hebben side effects in MySQL, die wil je niet altijd.

[ Voor 11% gewijzigd door Grijze Vos op 25-10-2007 15:31 ]

Op zoek naar een nieuwe collega, .NET webdev, voornamelijk productontwikkeling. DM voor meer info


Acties:
  • 0 Henk 'm!

  • Erkens
  • Registratie: December 2001
  • Niet online

Erkens

Fotograaf

Grijze Vos schreef op donderdag 25 oktober 2007 @ 15:30:
[...]

Euhm, volgens mij niet. Timestamps hebben side effects in MySQL, die wil je niet altijd.
over welke side effects hebben we het dan?

Acties:
  • 0 Henk 'm!

  • Grijze Vos
  • Registratie: December 2002
  • Laatst online: 28-02 22:17
Een timestamp (precies een, overigens), wordt geupdate naar now() als je row wordt geupdate.

Last time I checked iig.

[update]
Hmz, zo te zien is dat sinds versie 5.x anders.

[ Voor 20% gewijzigd door Grijze Vos op 25-10-2007 15:41 ]

Op zoek naar een nieuwe collega, .NET webdev, voornamelijk productontwikkeling. DM voor meer info


Acties:
  • 0 Henk 'm!

  • _Gekkie_
  • Registratie: Oktober 2000
  • Laatst online: 24-06 20:21

_Gekkie_

And the cow said: Helloooooow?

Ik zelf hanteer vaak een goede structuur voor het inkorten van grotere switches zodat je cases samen kunt nemen of variabelen maar 1 keer hoeft te definieren...

Mijn persoonlijke stelregel is eigenlijk: als je code ergens dubbel ziet moet het intelligenter kunnen :)
(DRY technieken)

offtopic:
Even een korte side-track vraag: Hoe zit het het met queries die op een datetime-kolom zitten in een nogal grote database? Kun je deze indexen om zodanig snelheidswinst te halen uit je queries?


Overigens: sideeffect van timestamps in MySQL: het is niet hetzelfde als de UNIX-timestamp notatie ;) dus dan zit je weer met FROM_UNIXTIME-functies voordat je ermee in PHP kunt werken...

Het automatisch updaten kun je in v5 van MySQL wel aan/uit zetten, dus dat is dan wel weer handig... (IMHO)

[ Voor 20% gewijzigd door _Gekkie_ op 25-10-2007 16:47 ]

Gekkie is a proud member of TheBenny!


Acties:
  • 0 Henk 'm!

  • Japius
  • Registratie: April 2003
  • Laatst online: 30-08 20:57
_Gekkie_ schreef op donderdag 25 oktober 2007 @ 16:43:
offtopic:
Even een korte side-track vraag: Hoe zit het het met queries die op een datetime-kolom zitten in een nogal grote database? Kun je deze indexen om zodanig snelheidswinst te halen uit je queries?
Ja, je kan ook gewoon een index op een datetime zetten. Kost natuurlijk wel performance, bij elke insert. Dat moet je afwegen tegen het voordeel dat de index oplevert: als er slecht eenmaal per dag een cron draait die stats genereert, kan het zijn dat je die index achterwege wilt laten.

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Mooie discussie, maar kan iemand ook even ingaan op mijn laatste vraag :)? Ik zit nog steeds met hetzelfde probleem namelijk...

Acties:
  • 0 Henk 'm!

  • Japius
  • Registratie: April 2003
  • Laatst online: 30-08 20:57
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mysql> select yearweek(curdate());
+---------------------+
| yearweek(curdate()) |
+---------------------+
|              200742 | 
+---------------------+
1 row in set (0.00 sec)

mysql> select yearweek(curdate())-43;
+------------------------+
| yearweek(curdate())-43 |
+------------------------+
|                 200699 | 
+------------------------+
1 row in set (0.00 sec)


Gaat niet helemaal lekker dus..


Edit: daarmee was je vraag beantwoord. Je kan natuurlijk wel
code:
1
date_sub(current_date(), interval 1 week)

doen.

[ Voor 13% gewijzigd door Japius op 25-10-2007 18:00 ]


Acties:
  • 0 Henk 'm!

  • FragFrog
  • Registratie: September 2001
  • Laatst online: 09:34
Grijze Vos schreef op donderdag 25 oktober 2007 @ 15:39:
Een timestamp (precies een, overigens), wordt geupdate naar now() als je row wordt geupdate.

Last time I checked iig.

[update]
Hmz, zo te zien is dat sinds versie 5.x anders.
Onzin, een timestamp kun je een 'ON UPDATE CUR_TIME' flag meegeven, dan wordt'ie elke keer geupdate naar de huidige tijd als je de rij aanpast. Als je een fatsoenlijke SQL admin tool (zoals Navicat of MySQL's Query Browser) hebt kun je dat gewoon zien en uitzetten :) Niet dat ik het gebruik van timestamp fields wil promoten trouwens, een date of datetime field is veelal beter.

[ Site ] [ twitch ] [ jijbuis ]


Acties:
  • 0 Henk 'm!

  • Erkens
  • Registratie: December 2001
  • Niet online

Erkens

Fotograaf

FragFrog schreef op donderdag 25 oktober 2007 @ 19:08:
[...]

Onzin, een timestamp kun je een 'ON UPDATE CUR_TIME' flag meegeven, dan wordt'ie elke keer geupdate naar de huidige tijd als je de rij aanpast. Als je een fatsoenlijke SQL admin tool (zoals Navicat of MySQL's Query Browser) hebt kun je dat gewoon zien en uitzetten :) Niet dat ik het gebruik van timestamp fields wil promoten trouwens, een date of datetime field is veelal beter.
In de oudere MySQL versies had je die optie niet en werd de eerste timestamp kolom automatisch ge-update zodra je een update op die row deed en niet expliciet een value in die kolom zette.

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Japius schreef op donderdag 25 oktober 2007 @ 17:55:
Edit: daarmee was je vraag beantwoord. Je kan natuurlijk wel
code:
1
date_sub(current_date(), interval 1 week)

doen.
@Japius: bedankt voor je reaktie, maar met jouw suggestie krijg ik de resultaten van de afgelopen 7 dagen, niet van de vorige week.

Een voorbeeld: vandaag is het 25 oktober. Als ik de resultaten van de vorige week wil, zoek ik naar de resultaten tussen maandag 15 en zondag 21 oktober (aangenomen dat een week op maandag begint). De code hierboven zal me de resultaten geven vanaf 18 oktober (7 dagen teruggeteld van 25 oktober).

Anders gezegd, ik zoek het SQL equivalent van:
PHP:
1
2
$prev_week_start = date("Y-m-d", mktime(0, 0, 0, date('n'), date('j') - (date('w') + 7), date('Y')));
$prev_week_end = date("Y-m-d", mktime(0, 0, 0, date('n'), date('j') - (date('w') + 1), date('Y')));


Heeft iemand een suggestie?

[ Voor 16% gewijzigd door Verwijderd op 25-10-2007 19:27 ]


Acties:
  • 0 Henk 'm!

  • Erkens
  • Registratie: December 2001
  • Niet online

Erkens

Fotograaf

Verwijderd schreef op donderdag 25 oktober 2007 @ 19:21:
Een voorbeeld: vandaag is het 25 oktober. Als ik de resultaten van de vorige week wil, zoek ik naar de resultaten tussen maandag 15 en zondag 21 oktober (aangenomen dat een week op maandag begint). De code hierboven zal me de resultaten geven vanaf 18 oktober (7 dagen teruggeteld van 25 oktober).

Heeft iemand een suggestie?
gebruik de mysql functie "WEEK( )"

SQL:
1
SELECT * FROM tabel WHERE WEEK(`datefield`)=(WEEK(NOW())-1)

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Erkens schreef op donderdag 25 oktober 2007 @ 19:32:
[...]
gebruik de mysql functie "WEEK( )"

SQL:
1
SELECT * FROM tabel WHERE WEEK(`datefield`)=(WEEK(NOW())-1)
Dit gaat fout als de datum bijvoorbeeld 1 januari 2007 is:
code:
1
2
mysql > SELECT WEEK( '2007-01-01' ) -1
mysql > -1

Bij date_sub gaat dat wel goed:
code:
1
2
mysql > SELECT DATE_SUB('2007-01-01', INTERVAL 1 DAY)
mysql > 2006-12-31

Suggesties?

Acties:
  • 0 Henk 'm!

  • Raynman
  • Registratie: Augustus 2004
  • Laatst online: 15:51
Wil je niet gewoon dit:
SQL:
1
... WHERE WEEK(`date`) = WEEK(DATE_SUB(CURDATE(), INTERVAL 1 WEEK))


edit: wel liever YEARWEEK() dan waarschijnlijk

[ Voor 18% gewijzigd door Raynman op 25-10-2007 21:51 ]


Acties:
  • 0 Henk 'm!

  • Erkens
  • Registratie: December 2001
  • Niet online

Erkens

Fotograaf

Overigens bedenk ik me nu dat je niet alleen het weeknummer goed wilt hebben, maar ook het jaartal, maar ook dat gaat wat minder goed met een jaarwisseling. Kan je niet gewoon BETWEEN gebruiken?

Acties:
  • 0 Henk 'm!

  • Megamind
  • Registratie: Augustus 2002
  • Laatst online: 10-09 22:45
PHP:
1
2
3
4
<?php 
$prev_week_start = strtotime("7 days ago");
$prev_week_end = strtotime("Yesterday");
?> 


kan ook :P Maar ik zag pagina 2 niet }:O

[ Voor 15% gewijzigd door Megamind op 25-10-2007 21:24 ]


Acties:
  • 0 Henk 'm!

  • FragFrog
  • Registratie: September 2001
  • Laatst online: 09:34
Erkens schreef op donderdag 25 oktober 2007 @ 19:14:
[...]

In de oudere MySQL versies had je die optie niet en werd de eerste timestamp kolom automatisch ge-update zodra je een update op die row deed en niet expliciet een value in die kolom zette.
I stand corrected :)
The following items summarize TIMESTAMP initialization and updating properties prior to MySQL 4.1.2:

The first TIMESTAMP column in table row automatically is set to the current timestamp when the record is created if the column is set to NULL or is not specified at all.

The first TIMESTAMP column in table row automatically is updated to the current timestamp when the value of any other column in the row is changed, unless the TIMESTAMP column explicitly is assigned a value other than NULL.

If a DEFAULT value is specified for the first TIMESTAMP column when the table is created, it is silently ignored.

Other TIMESTAMP columns in the table can be set to the current TIMESTAMP by assigning NULL to them, but they do not update automatically.

Beginning with MySQL 4.1.2, you have more flexible control over when automatic TIMESTAMP initialization and updating occur and which column should have those behaviors:

For one TIMESTAMP column in a table, you can assign the current timestamp as the default value and the auto-update value. It is possible to have the current timestamp be the default value for initializing the column, for the auto-update value, or both. It is not possible to have the current timestamp be the default value for one column and the auto-update value for another column.

Any single TIMESTAMP column in a table can be used as the one that is initialized to the current date and time, or updated automatically. This need not be the first TIMESTAMP column.
Maargoed, dan hebben we het wel over MySQL voor 4.1.2, da's een beetje hetzelfde als nu nog PHP3 gebruiken :+

[ Site ] [ twitch ] [ jijbuis ]

Pagina: 1