[PHP & MYSQL] vriendelijke url in Mysql zonder id nummer

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • klaaz
  • Registratie: April 2000
  • Laatst online: 06:33

klaaz

it's me!

Topicstarter
Ik probeer een vriendelijke URL uit mySql te halen zonder gebruik te maken van ID nummers. Ik maak voor de opslag van de URL kenmerken gebruik van een veld in de database genaamd url_nl.

Ik kan natuurlijk het url_nl veld een unieke waarde geven maar dat betekent dat er bij dubbele waarden een waarde moet worden verzonnen of dat er een suffix toegevoegd wordt door mijn CMS.

Beide vindt ik geen mooie optie.

Ik wil daarom een URL in het geheel volgen en daarbij steeds vergelijken of de parent klopt. Dat moet natuurlijk recursief gebeuren om het gehele pad af te kunnen lopen.

Een voorbeeld is misschien duidelijker, stel ik heb de volgende twee paden:

1. http://www.site.nl/organisatie/afdeling/informatie
2. http://www.site.nl/support/afdeling/informatie

De url waarden worden d.m.v. een rewrite in een variabele gestopt:

$url1 = organisatie
$url2 = afdeling
$url3 = informatie

$url1 = support
$url2 = afdeling
$url3 = informatie

Als ik nu url_nl = $url3 uitvoer met LIMIT 1 krijg ik natuurlijk (afhankelijk van de ORDER BY) slechts 1 resultaat.

De oplossing is volgens mij om recursief door de resultaten te lopen vanaf $url3 tot $url1.

Dus url_nl = $url3, met de resultaten vervolgens queryen op de parent etc. etc.

Het wordt echter een behoorlijk uitgebreide functie waarbij ik mij afvraag of dit niet simpeler kan.

De tabel beschikt overigens ook over id en subid velden waarmee de velden hiërarchisch aan elkaar gekoppeld zijn.

Ik red mij redelijk met mySql maar ben geen goeroe. Ik hoef geen kant en klare oplossing maar ben eerlijk gezegd de weg een beetje kwijt. Een hint in de juiste richting wordt op prijs gesteld.

Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Je kunt toch de table op zichzelf joinen (en nogmaals en ...) voor elke parent en dan met een AND in de where clause de parent checken?

Voorbeeld, uit de losse pols:
SQL:
1
2
3
4
5
select ...
from mytable a
inner join mytable b on (a.parentid = b.id)
where a.url_nl = 'bar'
  and b.url_nl = 'foo'


Voor een /foo/bar
Wat je overigens ook zou kunnen schrijven als:
SQL:
1
2
3
4
select ...
from mytable a
inner join mytable b on (a.parentid = b.id) and (b.url_nl = 'foo')
where a.url_nl = 'bar'


Overigens: Dubbelen voorkom je door simpelweg url_nl en parentid tot combined unique key te bombarderen.

Ik maak me wel een beetje "zorgen" om de url_nl. Is mijn aanname dat er (evt.) ook nog een url_en en url_de bestaat correct? In dat geval wordt 't een tikkie lastiger...

[ Voor 87% gewijzigd door RobIII op 07-12-2010 13:26 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 13-09 09:39

Janoz

Moderator Devschuur®

!litemod

Ben je niet gewoon veel te moeilijk aan het doen? Hoeveel van dergelijke pagina's denk je uberhaupt te gaan krijgen? Hoe vaak denk je dat de structuur (dus niet de inhoud) van deze pagina's gaat wijzigen?

Maar goed, wanneer je het werkelijk op deze manier wilt doen, waarom wil je perse de scheiding laten bestaan? Waarom zou je in je database niet gewoon "support/afdeling/informatie" op kunnen nemen? Dan ben je met 1 query klaar en hoef je je ook geen zorgen te maken over dubbelen.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

  • ajakkes
  • Registratie: Maart 2004
  • Laatst online: 16-05 22:32

ajakkes

👑

Als ik het goed snap is organisaties een artikel met url_nl = organisaties en afdelingen een artikel met url_nl = afdelingen.

dus
ID, parent_ID, url_nl, inhoud
1, 0, organisaties, blabla
2, 1, afdeling, blabla
3, 2, informatie, blablablabla
6, 5, informatie, blablablablie

En hij wil dus informatie met grandparent_id 1 opvragen.

Klopt dit klaaz.
Of maak ik het probleem moeilijker dan het is?

👑


Acties:
  • 0 Henk 'm!

  • SvMp
  • Registratie: September 2000
  • Niet online
Meest eenvoudig en flexibel resultaat krijg je als je het geen bezwaar vindt om per URL meerdere queries uit te voeren. Je kunt dan recursie toepassen in je tabel, wat normalisatie oplevert en de mogelijkheid om elke gewenste diepte te gebruiken in je URL's. Eventueel kun je URL's koppelen aan ID's in Memcache voor performanceverbetering.

Overweeg deze tabelindeling:
SQL:
1
2
3
4
5
6
7
8
9
CREATE TABLE URLs (
   ID INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
   parentID INTEGER UNSIGNED NOT NULL REFERENCES URLs(ID),
   URLpart VARCHAR(255) NOT NULL,
   articleID INTEGER UNSIGNED NOT NULL,
   PRIMARY KEY ID,
   UNIQUE (ID),
   UNIQUE (parentID, URLpart)
);


Scan je URL van links naar rechts: In beginsel is je parentID 0 (je root). Zoek op naam + parentID.
Komt er nog een stukje? => ParentID vernieuwen en weer zoeken
Komt er geen stukje meer? => ArticleID verwijst naar je artikel/pagina. Is deze 0? Foutmelding of naar beginpagina.

Voorbeeld: /info/over_ons

Eerst zoeken op 'info' en parentID 0. Stel gevonden record heeft ID 1. ParentID wordt nu 1.
Zoeken op 'over_ons' en parentID 1. In articleID staat je artikelnummer/paginanummer.

Door articleID in record met ID 1 ook een waarde te geven wordt /info ook een geldige URL.
Eventueel kun je nog een veld toevoegen voor de taal in geval van meertaligheid.

Je rewrite rules kunnen hiermee heel simpel worden, namelijk alles achter de hostnaam wordt in één post-var gestopt en je script doet de rest.

Is flexibliteit minder van belang en het project klein, dan kun je ook overwegen om alles door rewrite-rules te laten regelen.

[ Voor 14% gewijzigd door SvMp op 07-12-2010 14:21 ]


Acties:
  • 0 Henk 'm!

Verwijderd

Ik zou, zoals Janoz al aangeeft gewoon een routing kolom, of zelfs een losse table maken die een pad (bijv. /info/over-ons/test) route naar een andere (non friendly) url, dan is het veel makkelijker querien en ben je niet eens gebonden aan een bepaalde structuur in je url.
Dit zou enigszin vergelijkbaar zijn met de manier waarop Drupal dit doet.


Indien je wel je huidige idee na wil leven zou ik in plaats van een parent-child constructie toch eens kijken naar een MPTT oplossing waarbij je een pad in 1 keer kan querien vanuit de root van het pad.

hier wat meer info over mptt :
http://articles.sitepoint...rarchical-data-database/3

Acties:
  • 0 Henk 'm!

  • klaaz
  • Registratie: April 2000
  • Laatst online: 06:33

klaaz

it's me!

Topicstarter
Erg interessant deze reacties, Ik zie in ieder geval al een paar hints waar ik zelf nog niet opgekomen was.

Ik wil idd informatie met grandparent_id 1 opvragen zoals ajakkes stelt. Dat kan echter ook grandgrandparent_id 1 zijn of uiteraard parent_id 1. De maximale diepte in mijn CMS is iig 5. Op elk level kan natuurlijk meerdere keren dezelfde waarde voorkomen. Één van de levels zal echter altijd uniek (en dus bepalend) zijn.

Ben op dit moment nog niet in staat er mee aan de slag te gaan, dat wordt op zijn vroegst vanavond. Ik wilde iig alvast even een teken van leven geven en jullie bedanken voor het meedenken!

Acties:
  • 0 Henk 'm!

  • Barleone
  • Registratie: Maart 2009
  • Nu online
Ik loop met het idee rond om het met een koppeltabel te doen. Misschien heeft * klaaz daar ook iets aan? Hoe wordt hier over gedacht?
Tabel 1: [page_id] [page_title] [friendly_url_title]
Tabel 2: [parent_id] [child_id]

D.m.v. de koppeltabel wordt het heel eenvoudig om een tree omhoog of omlaag te zoeken.

[ Voor 29% gewijzigd door Barleone op 07-12-2010 21:50 ]

Tweakers.net 6 nostalgie! - Wayback Machine
Have you tried turning it off and on again?


Acties:
  • 0 Henk 'm!

  • klaaz
  • Registratie: April 2000
  • Laatst online: 06:33

klaaz

it's me!

Topicstarter
Ondanks de zeer aantrekkelijk inner join oplossing heb ik het toch voorlopig anders opgelost. Ik kom er met die inner join namelijk niet snel genoeg uit, dat vergt nog meer onderzoek. Daarnaast heb ik een redelijk vast staande tabal structuur in de MySQL database, wijzigingen daarin komen pas in aanmerking bij de eerstvolgende update van mijn CMS.

Mijn systeem werkt maximaal met 5 niveaus in de URL:

vb: http://www,site.nl/niveau1/niveau2/niveau3/niveau4/niveau5

Deze worden middels rewrites verwerkt in de volgende GET variabelen:

obj = niveau1
id = niveau2
idd = niveau3
iddd = niveau4
idddd = niveau5

De nu gebruikte functie:

De globals bevatten de prefix van de tabel en de taalvariant van het menu_nl/title_nl/url_nl etc veld
$object is de tabelnaam zonder prefix. Uiteraard kan ik de URL in het geheel doorgeven naar de functie en deze daar ontleden maar in het belang van de snelheid en de vaste structuur van dit project heb ik de variabele namen even in een array gezet.

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
function getRecord($object, $obj, $id, $idd, $iddd, $idddd)  
{ 
    global $menu, $title, $url, $text, $tabelprefix;
    
    $_vars[1] = "id";
    $_vars[2] = "idd";
    $_vars[3] = "iddd";
    $_vars[4] = "idddd";
    
    // Haal obj op uit de tree (met laagste id1) (laagste level)
    $qry            = "SELECT * FROM ".$tabelprefix.$object." WHERE $url = '".$obj."' and visible='yes' ORDER BY id1 ASC LIMIT 1";
    $result     = mysql_query($qry) or die("Oeps! Query fout!!  MySQL zegt: ".mysql_error()." En PHP zegt: $error_msg");
    
    if(mysql_num_rows($result) > 0)
        {
            if($id != "")
                {
                    $_data = mysql_fetch_array($result);
                    // werk de overige variabelen af
                    foreach ($_vars as $key => $value)
                        {
                            $qry2       = "SELECT * FROM ".$tabelprefix.$object." WHERE id1 = '".$_data["id"]."' AND lower($url)='".${$value}."'";
                            $result2    = mysql_query($qry2)    or die("Query fout!  MySQL zegt: ".mysql_error()." En PHP zegt: $error_msg");
                            if (mysql_num_rows($result2) > 0)
                                {
                                    $_data      = mysql_fetch_array($result2, MYSQL_ASSOC);
                                }
                            else
                                {
                                    return $_data;
                                }
                        }
                }
            else
                {
                    return mysql_fetch_array($result, MYSQL_ASSOC);
                }
        }
}


Dit functioneert maar kan natuurlijk een stuk efficiënter. Daar ga ik echter op een later tijdstip mee aan de slag.

[ Voor 0% gewijzigd door RobIII op 08-12-2010 11:43 . Reden: Code tags gefixed, syntax highlighting FTW *O* :P ]


Acties:
  • 0 Henk 'm!

Verwijderd

@Barleone

door een koppeltabel vermeidt je geen lastige joins en heb je nog steeds het probleem dat je per niveau een join moet doen en dus vooraf moet bepalen hoe diep je daar in wil gaan.

Zoals ik al aangaf is er een makkelijkere manier van het opslaan van hierarchische data en dat is via MPTT (Modified Preorder Tree Traversal)

Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 13-09 09:39

Janoz

Moderator Devschuur®

!litemod

Ik mis nog een beetje waarom je perse een recursieve structuur bij wilt houden. Als ik kijk naar de hoeveelheid energie die je nu in dit onderdeel steekt, en dat afzet tegen waarvoor je het gebruikt (menu structuur van een standaard site) dan zie ik nogal een behoorlijke scheefgroei. Hoeveel elementen denk je daadwerkelijk in je tabel te krijgen? Zelfs met 10 verschillende afdelingen stelt het nog steeds helemaal niks voor. Vooral ook omdat de hele structuur zo statisch als ik weet niet wat is.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

  • klaaz
  • Registratie: April 2000
  • Laatst online: 06:33

klaaz

it's me!

Topicstarter
@thioz
Ik begrijp de voordelen van koppeltabellen en MPTT, dat is echter een te ingrijpende wijziging voor het huidige systeem om op korte termijn te overwegen.

@Janoz
Wat bedoel je met scheefgroei? De hoeveelheid elementen is idd beperkt tot maximaal een paar honderd en in principe statisch. Dat neemt niet weg dat hetzelfde systeem ook toegepast wordt in modules/onderdelen die minder statisch zijn. Mijn doel is uiteindelijk optimalisatie van de code maar soms moet ik met mijn (toch wel) beperkte SQL kennis even verder omdat de ontwikkeling anders stagneert. En dan krijg je een oplossing zoals hierboven omschreven (die voorlopig doet wat het moet doen).

Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 13-09 09:39

Janoz

Moderator Devschuur®

!litemod

Gebruik je op enig moment uberhaupt de boom structuur die je in de database hebt staan, anders dan in de bovenstaande code? Ik krijg namelijk het gevoel dat je een oplossingsrichting gekozen hebt die het je in dit deel heel moeilijk maakt (boomstructuren in SQL zijn lastig) terwijl er eigenlijk helemaal geen noodzaak voor is. Een statisch geconfigureerde boom structuur in php zal misschien minder 'hip' zijn, maar een stuk simpeler en efficiënter. En mocht hij redelijk groot worden dan kun je eventueel overwegen om een stukje software te schrijven die je lokaal draait om dit stukje php te genereren.

Het idee dat je exact deze code kunt blijven gebruiken in minder statische delen lijkt me compleet ongegrond. Stel je wilt iets met nieuwsberichten doen, dan zul je sowieso de hele code al om moeten gaan gooien omdat het meer is dan enkel een page id erbij zoeken. Dan zul je immers al een compleet ander script uit moeten gaan voeren.

Hoewel generieke aanpakken vaak als de heilige graal worden gezien blijkt in de praktijk juist dat dit vaak de molenstenen zijn waardoor grote projecten verzuipen. Eigenlijk is het het broertje van premature optimalisatie. Ik zeg niet dat je van te voren niet na moet denken over je ontwerp, maar je kunt ook uitbreidbare code maken, zonder dat het vanaf dag 1 de ultieme aanpak die alle andere aanpakken overbodig maakt wordt.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

  • mithras
  • Registratie: Maart 2003
  • Niet online
Waarom laad je niet in één keer je gehele sitemap in je systeem. Dat resultaat cache je en is verder eenvoudig te gebruiken.

Dus je hebt een tabel als volgt:
code:
1
2
3
4
5
6
7
id | url          | parent
1    organisatie    null
2    support        null
3    afdeling       1
4    afdeling       2
5    informatie     3
6    informatie     4


Deze laadt je in zijn geheel in met bijvoorbeeld een mapping van url naar page-id:
PHP:
1
2
3
4
5
6
7
8
array(
'iorganisatie' => 1,
'support' => 2,
'organisatie/afdeling' => 3,
'support/afdeling' => 4,
'organisatie/afdeling/informatie' => 5,
'support/afdeling/informatie' => 6
)


Je stopt de array gewoon in je cache en je kan bij alle requests een simpele lookup gebruiken. Verandert je iets aan je paginastructuur, markeer je je cache als invalid. Verder blijft het systeem snel werken en ben je enorm flexibel met url-parts in je database (in plaats van gehele urls).

Acties:
  • 0 Henk 'm!

  • klaaz
  • Registratie: April 2000
  • Laatst online: 06:33

klaaz

it's me!

Topicstarter
@janoz
De genoemde boomstructuur wordt in het CMS ook als boomstructuur getoond. Je kunt in de tree klikkend door de verschillende pagina's heen wandelen. Elk onderdeel, bijvoorbeeld een pulldown menu heeft een eigen tak met onderliggende items in de tree. Dit is jaaaren geleden een keer ontwikkeld en eigenlijk altijd afdoende gebleken voor de website structuur. Wijzigen kost een flinke hoeveelheid tijd en energie die ik helaas even niet voorradig heb. Dat moment zal echter zeker komen.

Voor nieuwsberichten, agendaitems e.d. worden separate tabellen gebruikt die vanwege de kans op herhaling wel met een id worden opgeroepen in de URL: bv: http:www.site.nl/nieuws/1245/titel-nieuwsbericht

In het CMS is elke onderdeel in een tabje te benaderen. Klanten zijn heel tevreden over de gebruiksvriendelijkheid er van. Dat het beter kan daar ben ik echter van overtuigd.

@mithras
Die optie is interessant en ga ik ook zeker samen met de andere opties in dit topic bekijken.

Acties:
  • 0 Henk 'm!

  • CH4OS
  • Registratie: April 2002
  • Niet online

CH4OS

It's a kind of magic

klaaz schreef op woensdag 08 december 2010 @ 11:14:
Ondanks de zeer aantrekkelijk inner join oplossing heb ik het toch voorlopig anders opgelost. Ik kom er met die inner join namelijk niet snel genoeg uit, dat vergt nog meer onderzoek. Daarnaast heb ik een redelijk vast staande tabal structuur in de MySQL database, wijzigingen daarin komen pas in aanmerking bij de eerstvolgende update van mijn CMS.

Mijn systeem werkt maximaal met 5 niveaus in de URL:

vb: http://www,site.nl/niveau1/niveau2/niveau3/niveau4/niveau5

Deze worden middels rewrites verwerkt in de volgende GET variabelen:

obj = niveau1
id = niveau2
idd = niveau3
iddd = niveau4
idddd = niveau5

De nu gebruikte functie:

De globals bevatten de prefix van de tabel en de taalvariant van het menu_nl/title_nl/url_nl etc veld
$object is de tabelnaam zonder prefix. Uiteraard kan ik de URL in het geheel doorgeven naar de functie en deze daar ontleden maar in het belang van de snelheid en de vaste structuur van dit project heb ik de variabele namen even in een array gezet.

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
function getRecord($object, $obj, $id, $idd, $iddd, $idddd)  
{ 
    global $menu, $title, $url, $text, $tabelprefix;
    
    $_vars[1] = "id";
    $_vars[2] = "idd";
    $_vars[3] = "iddd";
    $_vars[4] = "idddd";
    
    // Haal obj op uit de tree (met laagste id1) (laagste level)
    $qry            = "SELECT * FROM ".$tabelprefix.$object." WHERE $url = '".$obj."' and visible='yes' ORDER BY id1 ASC LIMIT 1";
    $result     = mysql_query($qry) or die("Oeps! Query fout!!  MySQL zegt: ".mysql_error()." En PHP zegt: $error_msg");
    
    if(mysql_num_rows($result) > 0)
        {
            if($id != "")
                {
                    $_data = mysql_fetch_array($result);
                    // werk de overige variabelen af
                    foreach ($_vars as $key => $value)
                        {
                            $qry2       = "SELECT * FROM ".$tabelprefix.$object." WHERE id1 = '".$_data["id"]."' AND lower($url)='".${$value}."'";
                            $result2    = mysql_query($qry2)    or die("Query fout!  MySQL zegt: ".mysql_error()." En PHP zegt: $error_msg");
                            if (mysql_num_rows($result2) > 0)
                                {
                                    $_data      = mysql_fetch_array($result2, MYSQL_ASSOC);
                                }
                            else
                                {
                                    return $_data;
                                }
                        }
                }
            else
                {
                    return mysql_fetch_array($result, MYSQL_ASSOC);
                }
        }
}


Dit functioneert maar kan natuurlijk een stuk efficiënter. Daar ga ik echter op een later tijdstip mee aan de slag.
Ik zie het vaak gebeuren, maar de foutafhandeling mag ook wel een tikkie beter.
Wat er nu gebeurd, als MySQL fout gaat is dat direct het script afgebroken word en dan gewoon klakkeloos de foutmelding weergeeft op de webpagina. Op zich weet je dan natuurlijk nog niet zoveel, maar het is gewoon mooier om de fout af te vangen en vervolgens de normale opbouw van de pagina te laten zien, met dan een (bewerkte) foutmelding er in.

Wat ik ook mis, hoe vang je nu 404's af? Als ik het zo zie, kun je nu geen 404 krijgen en kom je altijd op een pagina uit?

[ Voor 69% gewijzigd door CH4OS op 08-12-2010 17:52 ]


Acties:
  • 0 Henk 'm!

  • klaaz
  • Registratie: April 2000
  • Laatst online: 06:33

klaaz

it's me!

Topicstarter
Rondom de functie zelf is foutafhandeling gebouwd, als het resultaat geen array is is er sprake van een fout en dus een nette afhandeling in de layout van de site zelf. Geen 404 trouwens, dat staat nog op het verlanglijstje ;)

Acties:
  • 0 Henk 'm!

  • SvMp
  • Registratie: September 2000
  • Niet online
mithras schreef op woensdag 08 december 2010 @ 15:07:
Waarom laad je niet in één keer je gehele sitemap in je systeem. Dat resultaat cache je en is verder eenvoudig te gebruiken. <knip>
Zodra de website heel groot wordt, wordt de data in de cache te groot.
Als je ook nog eens met meerdere gebruikers werkt die regelmatig een artikel toevoegen/verwijderen, dan is een update relatief 'duur' omdat de hele cache wordt gewist. Voordeel van het idee is de eenvoud, maar alleen geschikt voor iets kleins. De optie die ik een paar dagen geleden al opperde, is vrijwel niet trager, maar wel volledig flexibel, informatie wordt niet dubbel opgeslagen, en wijzigingen van namen van url-delen die hoger in de boom staan zijn gemakkelijker omdat de naam slechts één keer in de database staat. Enige nadeel is dat het iets meer werk is om te programmeren, maar wel prima geschikt voor hergebruik.

[ Voor 5% gewijzigd door SvMp op 09-12-2010 09:25 ]


Acties:
  • 0 Henk 'm!

  • SvMp
  • Registratie: September 2000
  • Niet online
klaaz schreef op woensdag 08 december 2010 @ 11:14:
Ondanks de zeer aantrekkelijk inner join oplossing heb ik het toch voorlopig anders opgelost. Ik kom er met <knip>
Dit functioneert maar kan natuurlijk een stuk efficiënter. Daar ga ik echter op een later tijdstip mee aan de slag.
Een paar opmerkingen:
- Voor elk object een eigen tabel, volgens mij is het veel eenvoudiger om één tabel te hanteren waarbij object een veld is. Maar als je met verwijzingen naar een parent werkt (zie meerdere adviezen in dit topic) dan kun je zelfs een apart veld voor een object vermijden, door bijvoorbeeld object op het rootlevel te plaatsen.
- Ik raad prepared-statements aan voor je SQL-query, genoeg over te vinden op Google. Je hoeft dan geen variabelen meer in de query te zetten, SQL-injection is dan uitgesloten. Bovendien is het ideaal in het geval van een herhaling van dezelfde query.
- Ook al is nu je verwachting dat er maximaal 5 levels zijn, ik vind een oplossing met een vast aantal levels te rigide. Terwijl een meer flexibele oplossing niet complexer of groter hoeft te zijn.
- Ik vind persoonlijk - kan dus anders over gedacht worden - het gebruik van globale variabelen in PHP not-done. Ik doe het niet meer, m.u.v. de standaard super-globals. Globals kun je vermijden met object georiënteerd programmeren.

[ Voor 57% gewijzigd door SvMp op 09-12-2010 11:14 . Reden: Toevoeging punt over globale variabelen ]


Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 13-09 09:39

Janoz

Moderator Devschuur®

!litemod

SvMp schreef op donderdag 09 december 2010 @ 09:25:
[...]


Zodra de website heel groot wordt, wordt de data in de cache te groot.
Dat valt heel erg mee
Als je ook nog eens met meerdere gebruikers werkt die regelmatig een artikel toevoegen/verwijderen, dan is een update relatief 'duur' omdat de hele cache wordt gewist.
Onzin. Php is nog steeds een request georienteerde omgeving. Sowieso wordt de cache elk request opnieuw opgebouwd. Mocht je daadwerkelijk dingen als shared memory toe gaan passen, dan is het legen van de cahce nog steeds triviaal.
Voordeel van het idee is de eenvoud, maar alleen geschikt voor iets kleins. De optie die ik een paar dagen geleden al opperde, is vrijwel niet trager, maar wel volledig flexibel, informatie wordt niet dubbel opgeslagen, en wijzigingen van namen van url-delen die hoger in de boom staan zijn gemakkelijker omdat de naam slechts één keer in de database staat. Enige nadeel is dat het iets meer werk is om te programmeren, maar wel prima geschikt voor hergebruik.
Het punt is dat we van Klaaz nog steeds niet de achterliggende requirements hebben gekregen. Voor zover ik zie zijn er dan twee mogelijkheden:
1 - De boom structuur wordt daadwerkelijk ook echt gebruikt voor bijvoorbeeld het menu, maar in dat geval zul je altijd de hele boom in het geheugen moeten hebben.
2 - De boom structuur wordt eigenlijk helemaal niet gebruikt. In dat geval is er helemaal geen noodzaak om zo ingewikkeld te doen. Een simpele tabel met URI en ID is voldoende (eventuele spaarzame hiërarchische operaties kun je in de database nog uitvoeren met een LEFT op het URI veld)

Nogmaals. Boom structuren zijn erg leuk, maar wil tricky in SQL. Het is leuk om er mee te spelen, maar zorg wel dat je een reden hebt om ze te gebruiken. Die heb ik van Klaaz nog niet gehoord.
klaaz schreef op woensdag 08 december 2010 @ 23:04:
Rondom de functie zelf is foutafhandeling gebouwd, als het resultaat geen array is is er sprake van een fout en dus een nette afhandeling in de layout van de site zelf. Geen 404 trouwens, dat staat nog op het verlanglijstje ;)
Het ging oa ook om de 'or die'-s die door de code heen staan.

[ Voor 9% gewijzigd door Janoz op 09-12-2010 10:36 ]

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

  • Spiral
  • Registratie: December 2005
  • Niet online
Wellicht kan Group _Concat ook helpen

To say of what is that it is not, or of what is not that it is, is false, while to say of what is that it is, and of what is not that it is not, is true. | Aristoteles


Acties:
  • 0 Henk 'm!

  • SvMp
  • Registratie: September 2000
  • Niet online
Janoz schreef op donderdag 09 december 2010 @ 10:35:
Onzin. Php is nog steeds een request georienteerde omgeving. Sowieso wordt de cache elk request opnieuw opgebouwd. Mocht je daadwerkelijk dingen als shared memory toe gaan passen, dan is het legen van de cahce nog steeds triviaal.
Ik bedoel een systeem als Memcache, wat je idd als shared memory zou kunnen zien. Het legen van de cache is in alle algoritmen nodig bij wijzigingen, maar niet de volledige cache. Alleen het record wat je hebt gewijzigd. Bij een hierarchische structuur knip je het geheel namelijk in kleine stukjes.
Nogmaals. Boom structuren zijn erg leuk, maar wil tricky in SQL. Het is leuk om er mee te spelen, maar zorg wel dat je een reden hebt om ze te gebruiken. Die heb ik van Klaaz nog niet gehoord.
Hangt van het soort boom-structuur af. SQL-tabellen die verwijzen naar een parent zijn m.i. niet tricky, zolang de hierarchie geen tientallen niveaus diep gaat. Een friendly-URL opknippen in kleine stukjes zal niet meer dan 10 niveaus zijn. Icm. met een goede cache, verwacht ik geen problemen.

Ik heb een soortgelijk systeem gebouwd met een plaatjesdatabase en een hierarchie die niet alleen Google-vriendelijke links geeft, maar ook gemakkelijk zoekt en navigeert in de back-office. Ik heb daar geen enkele beperking ingebouwd qua omvang en diepte, al zal je in de praktijk geen tientallen niveaus diep moeten gaan om het 'leuk' te houden. Het functioneert goed.

[ Voor 13% gewijzigd door SvMp op 09-12-2010 10:46 ]


Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
SvMp schreef op donderdag 09 december 2010 @ 09:25:
Zodra de website heel groot wordt, wordt de data in de cache te groot.
Dan, "zodra de site heel groot wordt", schroef je de cache grootte omhoog :? Zet je wat extra resources in :?
SvMp schreef op donderdag 09 december 2010 @ 09:25:
Als je ook nog eens met meerdere gebruikers werkt die regelmatig een artikel toevoegen/verwijderen, dan is een update relatief 'duur' omdat de hele cache wordt gewist.
Dit kan ik even, compleet, niet volgen :? Waarom zou de hele cache gewist worden als een gebruiker een artikel toevoegt / verwijderd? En waarom is een cache van artikelen überhaupt gerelateerd aan een menustructuur?

edit:

Laaaaaaat :X Tabjes open laten staan :P

[ Voor 3% gewijzigd door RobIII op 09-12-2010 11:16 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

  • mithras
  • Registratie: Maart 2003
  • Niet online
Het komt redelijk overeen met Janoz reactie, maar ik wil toch even reageren :)
SvMp schreef op donderdag 09 december 2010 @ 09:25:
[...]


Zodra de website heel groot wordt, wordt de data in de cache te groot.
Wat is "te groot" en hoe definieer jij dat? Ik heb dit principe toegepast voor al mijn ZF-based apps waar ik de gehele routing structure cache. De cache is 16K groot en bevat een gehele sitemap in zowel Zend_Controller_Route_Route objecten als Zend_Navigation_Page objecten. Een plain array is nog een stuk kleiner ook.
Als je ook nog eens met meerdere gebruikers werkt die regelmatig een artikel toevoegen/verwijderen, dan is een update relatief 'duur' omdat de hele cache wordt gewist. Voordeel van het idee is de eenvoud, maar alleen geschikt voor iets kleins.
Nogmaals: meten is weten. Ik denk dat je onderschat wat je ermee kan en tot hoe groot je het kan opschalen. Voor een goede site kan je als uitgangspunt nemen dat je meer pageviews hebt dan wijzigingen in je site, dus je hebt al snel voordeel te pakken. Verder ga ik hier uit van plain pagina's. Als je blogs e.d. gaat opnemen in je router, zou ik de blog-root als route nemen en de blogmodule de verdere url's laten afhandelen. Dus niet voor elk blogartikel een route, maar voor elke blog.
De optie die ik een paar dagen geleden al opperde, is vrijwel niet trager, maar wel volledig flexibel, informatie wordt niet dubbel opgeslagen, en wijzigingen van namen van url-delen die hoger in de boom staan zijn gemakkelijker omdat de naam slechts één keer in de database staat. Enige nadeel is dat het iets meer werk is om te programmeren, maar wel prima geschikt voor hergebruik.
Er zijn een hoop nadelen. Een query per url-part en dat altijd voor elk request is gewoon een enorm slecht idee. Performanceimpact is wellicht geen issue voor 10 bezoekers per dag met 20 pagina's in de site. Als je 1000 pagina's hebt met 1e6 bezoekers per dag heeft dat toch echt wel impact. Queries zijn op deze manier enorm kostbaar. Je kan beter eenmalig de php mapping structure opbouwen en dat vervolgens cachen. Je hebt per slot van rekening de sitemap ook nodig voor title's, navigatiemenu's, breadcrumbs etc.

Verder is in beide oplossingen (mijn en jouw) het idee dat een child de url opbouwt via de url-parts van de parents. Ook in mijn scheme heeft een subsub-pagina niet de gehele url, maar slechts zijn eigen url-part.
SvMp schreef op donderdag 09 december 2010 @ 10:44:

[...]


Hangt van het soort boom-structuur af. SQL-tabellen die verwijzen naar een parent zijn m.i. niet tricky, zolang de hierarchie geen tientallen niveaus diep gaat. Een friendly-URL opknippen in kleine stukjes zal niet meer dan 10 niveaus zijn. Icm. met een goede cache, verwacht ik geen problemen.
Lees anders het artikel van crisp eens op zijn Tweakblog: Crisp's blog: Formatting a multi-level menu using only one query. Zoveel queries voor zoiets simpels is enorm overrated en een grote bottleneck voor een applicatie.

Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 13-09 09:39

Janoz

Moderator Devschuur®

!litemod

SvMp schreef op donderdag 09 december 2010 @ 10:44:
Hangt van het soort boom-structuur af. SQL-tabellen die verwijzen naar een parent zijn m.i. niet tricky,
Tricky is niet hetzelfde als onmogelijk. Tricky is niet triviaal. Ja er zijn vele oplossingen om alsnog een parent child structuur in een database te krijgen. Maar sommige operaties zijn toch wel net ietsje ingewikkelder in SQL dan dat het in normale code zou zijn. De hele route naar de root is in een normale programmeertaal middels een recursieve funtcie in 3 regels te doen zonder dat je iets vreemds met je node hoeft te doen (enkel een parent pointer). Vergelijk dat nu eens met een SQL oplossing. Dan heb je, of een DB met ondersteuning voor dit soort structuren, of een ingewikkeldere database structuur met bijbehorende aangepaste update en insert methodieken, of een maximale diepte, of code waarin je meerdere keren queries op de database af loopt te vuren.


Maar op de echte vraag wordt nog steeds niet ingegaan... Waarom is (in deze context) de boom structuur nodig. Ik heb nog steeds geen valide reden gezien.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

  • mithras
  • Registratie: Maart 2003
  • Niet online
Janoz schreef op donderdag 09 december 2010 @ 12:48:
[...]

Maar op de echte vraag wordt nog steeds niet ingegaan... Waarom is (in deze context) de boom structuur nodig. Ik heb nog steeds geen valide reden gezien.
Ik neem aan dat je met het tegenovergestelde bedoelt, dat de gehele url in de specifiieke entry van de pagina staat. Dus:
organisatie => #1
organisatie/afdeling => #3
organisatie/afdeling/support => #1234

Want het lijkt me evident dat dit niet gewenst is. Een kleine wijziging in een "parent" (ook al is die niet precies vastgelegd) betekent dat je al die entries moet wijzigen (of bij een "child" ineens een andere url krijgt).

Verder wil je toch graag hierarchie in je db meegeven, omdat je in een sitemap ook vaak met hierarchie in pagina's werkt. Je hebt breadcrumbs, menu's etc. en daar wil je de hierarchie van weten, dus zal je dat ook moeten vastleggen. Als je dan bovenstaande koppeling neemt, maar toch de hierarchie in je database verwerkt, kan je volgens mij net zo goed doornormaliseren waar je url-parts gebruikt ipv gehele url's :)

Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 13-09 09:39

Janoz

Moderator Devschuur®

!litemod

Gezien de voorbeelden die de TS geeft lijkt de sitemap mij redelijk statisch. De boel wordt dus ontiegelijk veel vaker uitgelezen dan dat er wijzigingen plaats vinden. Daarnaast verwacht ik niet dat een parent vaak aangepast wordt, en wanneer dat gebeurt is het nog steeds goed te doen (met een beetje substring magic is dat in 1 query te doen)
Verder wil je toch graag hierarchie in je db meegeven
Ik wil iets alleen graag als er ook een reden voor is. Uiteraard is een hierarchie in een database steken een heerlijke vingeroefening, maar de vraag blijft: Is het nodig? De TS wil een stuk gegevens opslaan in een database en dat vervolgens weer uitvragen. De enige vraag die tot nu toe gedefinieerd is, is "Gegeven een URI, doe mij een pagina ID". Zolang er geen duidelijkheid is mbt wat er verder nodig is kunnen we wel van alles gaan verzinnen, maar het enige wat daarmee gedaan wordt is een overmatig complexe implementatie maken zonder dat er ook maar 1 valide onderbouwing is over waarom dit zo complex zou moeten.

[ Voor 3% gewijzigd door Janoz op 09-12-2010 13:13 ]

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

  • ajakkes
  • Registratie: Maart 2004
  • Laatst online: 16-05 22:32

ajakkes

👑

Als TS uitgelegd wordt welke methodes er zijn om iets te bereiken is het aan TS om de afweging te maken of iets nodeloos complex is of een nuttige vingeroefening met voordelen.

Jij bent natuurlijk niet degene die dat voor hem moet gaan beslissen. Je kan aangeven dat er eenvoudige methodes zijn om deze complexiteit te omzeilen. Je kan vragen naar beweegredenen om voor de oplossing te kiezen zodat je een eenvoudigere oplossing kan aandragen. Maar het blijft de beslissing van TS om voor een bepaalde methode te kiezen. En het blijft jouw beslissing om oplossingen aan te dragen.

Maar voor de derde keer aandragen dat de methode volgens jou, naar de omstandigheden die je vermoedt, te complex is en dat TS voor een simpele methode moet kiezen. Zonder deze aan te dragen lijkt mij een beetje zinloos.

👑


Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 13-09 09:39

Janoz

Moderator Devschuur®

!litemod

Ik zie het liever andersom. Zolang de TS niet aan geeft/kan geven waarom de complexe oplossing nodig is promoot ik de simpele. En ik heb meerdere malen aan de topicstarter gevraagd wat hij nu eigenlijk nog meer wil. Lees daarnaast ook goed wat ik in laatste alinea van deze post aangeef.

Overcomplicatie is de doodsteek van productiviteit. Je moet dingen niet ingewikkeld gaan maken omdat je denkt dat het in de toekomst misschien wel handig kan zijn. Hier is dat net zo. Steek alles weg achter een methode getPageId(uri) waarbij de uri een string zou kunnen zijn, maar eventueel ook de eerder aangehaalde array van path elementen. Mocht er in de toekomst blijken dat er inderdaad iets lastigers nodig is, dan kan op dat moment alsnog de boom geimplementeerd worden. Aan de interface hoeft niks veranderd te worden dus de aanpassing is behoorlijk contained en redelijk simpel door te voeren. Je sluit dus niet de weg af naar de ingewikkelde implementatie, je gaat hem gewoon alleen doen op het moment dat er ook daadwerkelijk een noodzaak voor is.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

  • SvMp
  • Registratie: September 2000
  • Niet online
Janoz schreef op donderdag 09 december 2010 @ 12:48:

Maar op de echte vraag wordt nog steeds niet ingegaan... Waarom is (in deze context) de boom structuur nodig. Ik heb nog steeds geen valide reden gezien.
Misschien niet door TS, ik heb wel argumenten gehoord. Natuurlijk moet je niet onnodig gecompliceerd doen, maar de boom structuur is m.i. amper complexer dan een platte lijst. Flexibiliteit heeft wel waarde. Vaak zegt een klant/opdrachtgever eerst dat het klein blijft, maar in de praktijk komt er toch meer en meer bij. Op mijn werk bouw ik als het niet te veel extra tijd kost flexibiliteit in, aangezien mijn baas elke maand iets anders vindt. Ik zie van flexibiliteit af als het enorm veel werk of om de performance de nek om draait.

Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 13-09 09:39

Janoz

Moderator Devschuur®

!litemod

SvMp schreef op donderdag 09 december 2010 @ 16:51:
Misschien niet door TS, ik heb wel argumenten gehoord.
Welke dan? En wiens probleem zijn we aan het oplossen?
Natuurlijk moet je niet onnodig gecompliceerd doen, maar de boom structuur is m.i. amper complexer dan een platte lijst.
D'r zit toch een behoorlijk verschil in. platte lijst is een paar regels. Kijk eens naar de lappen code die hier geplaatst worden, en dat zijn dan nog niet eens efficiente oplossingen.
Flexibiliteit heeft wel waarde.
Ja, maar er is een verschil tussen rekening houden met flexibiliteit en generiek implementeren omdat je denkt de flexibiliteit ooit nodig te zullen gaan hebben, zonder dat er ook maar enige requirement beschikbaar is die aangeeft dat het er aan zit te komen.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

  • ajakkes
  • Registratie: Maart 2004
  • Laatst online: 16-05 22:32

ajakkes

👑

Janoz schreef op donderdag 09 december 2010 @ 16:56:
[...]

Welke dan? En wiens probleem zijn we aan het oplossen?

[...]

D'r zit toch een behoorlijk verschil in. platte lijst is een paar regels. Kijk eens naar de lappen code die hier geplaatst worden, en dat zijn dan nog niet eens efficiente oplossingen.

[...]

Ja, maar er is een verschil tussen rekening houden met flexibiliteit en generiek implementeren omdat je denkt de flexibiliteit ooit nodig te zullen gaan hebben, zonder dat er ook maar enige requirement beschikbaar is die aangeeft dat het er aan zit te komen.
Lappen code waar na implementatie minimaal een half jaar niet bekeken hoeft te worden en door de eindgebruikers nooit bekeken hoeft te worden of een platte tekst die elke keer dat de eindgebruiker iets wijzigt gewijzigd moet worden door iemand met kennis van zaken.

Dat jij het nut van de boomstructuur niet ziet wil niet zeggen dat het geen nut heeft. Dat TS niet op jouw vraag ingaat wil niet zeggen dat hij hem niet gehoord heeft. Maar volgens mij hoeft TS geen redenen te gaan herhalen die door anderen genoemd worden om niet voor jouw oplossing te kiezen. Een oplossing die ik nog niet eens langs heb zien komen.

Ik heb inmiddels begrepen dat jij niet aan de huidige oplossing van TS mee wil helpen. Ik heb het genoteerd.
Ik heb ook het idee dat TS niet voor jouw oplossing wil kiezen omdat hij van mening is deze volgens jou zeer complexe code toch nodig is voor zijn project.

Ik ben erg benieuwd naar de complexe oplossing.

👑


Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 13-09 09:39

Janoz

Moderator Devschuur®

!litemod

ajakkes schreef op donderdag 09 december 2010 @ 17:14:
Lappen code waar na implementatie minimaal een half jaar niet bekeken hoeft te worden en door de eindgebruikers nooit bekeken hoeft te worden of een platte tekst die elke keer dat de eindgebruiker iets wijzigt gewijzigd moet worden door iemand met kennis van zaken.
Wanneer klaaz (of zijn opvolger) over een half jaar de code weer open trekt en daar de functie 'getRecord' ziet staan heeft hij wel even nodig om uit te zoeken wat er aan de hand is. En dan is er nog een redelijk straight forward hierarchische implementatie gekozen.

Daarnaast heb ik nergens gezegd dat het perse platte tekst moet zijn, maar dat dit een afweging moet zijn. Wanneer er pas over een half jaar een wijziging is dan is dat goed met een platte tekst of een uitgeschreven array te doen. Als dat niet zo is is ook een simpele mapping tussen uri en id goed onderhoudbaar. Is op elke pagina het menu nodig (en dus de complete boomstructuur) dan is het veel handiger om 1x de hele boom in te lezen en die datastructuur ook bij het bepalen van de pagina id te gebruiken.
Dat jij het nut van de boomstructuur niet ziet wil niet zeggen dat het geen nut heeft. Dat TS niet op jouw vraag ingaat wil niet zeggen dat hij hem niet gehoord heeft. Maar volgens mij hoeft TS geen redenen te gaan herhalen die door anderen genoemd worden om niet voor jouw oplossing te kiezen. Een oplossing die ik nog niet eens langs heb zien komen.
Ik zie wel degelijk nut in boomstructuren. Erg handig wanneer je snel elementen in een verzameling wilt zoeken, maar dat gebeurt hier niet. Ja het gebeurt wel, maar middels tooling (SQL) waarbij je niet die voordelen hebt die een boomstructuur je normaal zouden bieden. Het opzoeken gaat dus alleen maar langzamer terwijl je code een stuk ingewikkelder (= slechter te onderhouden en duurt langer om te implementeren). Daarnaast heb ik nog helemaal niemand hier een reden zien noemen waarom een boomstructuur in de database echt nodig is, maar misschien heb ik er overheen gelezen en kun je ze even herhalen. In het kort, wat is de meerwaarde van die boomstructuur in deze situatie?
Ik heb inmiddels begrepen dat jij niet aan de huidige oplossing van TS mee wil helpen. Ik heb het genoteerd.
Ik heb ook het idee dat TS niet voor jouw oplossing wil kiezen omdat hij van mening is deze volgens jou zeer complexe code toch nodig is voor zijn project.
De vraag is waar de topicstarter meer mee geholpen is. De valkuil waar ik veel mensen in zie stappen is dat ze een probleem hebben en vervolgens een oplossings richting kiezen. Halverwege die oplossing komen ze bij een hobbel. Vervolgens zijn ze druk bezig die hobbel te nemen en roepen daar eventueel hulp bij in. Ik daag vervolgens de TS (en anderen) om eens een stapje terug te doen. Is de initieel gekozen oplossingsrichting eigenlijk wel de beste?
Ik ben erg benieuwd naar de complexe oplossing.
Dan moet je het hier eerder aangehaalde blog van crisp gaan lezen. Echter is een simpelere oplossing beter dan een complexe oplossing. Houdt Einstein daarbij wel in het achterhoofd: "Maak alles zo simpel mogelijk, maar niet simpeler".

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'

Pagina: 1