Digicted

Archief voor PHP

Een patch voor het cache path probleem in PHPDelicious

Wednesday, December 24th, 2008

Voor mijn stage bij Bemba was ik bezig met de del.icio.us API in PHP, waardoor ik natuurlijk koos voor de PHPDelicious library. Helaas zat er een foutje in de originele library, waardoor er niet gecached kon worden als het cache path niet expliciet werd meegegeven aan de constructor.

De fix voor dit probleem is gelukkig erg simpel. In cache.inc.php zoek je naar de volgende regel:

public function __construct($sKey, $iCacheTime, $sPrefix='', $sCachePath = CACHE_PATH) {

Dit zou normaal regel 44 moeten zijn. Het probleem zit ‘m in CACHE_PATH, wat eigenlijk PHP_DELICIOUS_CACHE_PATH zou moeten zijn. De nieuwe regel wordt dus:

public function __construct($sKey, $iCacheTime, $sPrefix='', $sCachePath = PHP_DELICIOUS_CACHE_PATH) {

Na deze kleine wijziging is alles ok. Ik heb een patch gestuurd naar de auteur, en naar mijn ervaring wordt de library wel gepatched.

De patch kan toegepast worden door de patch-file in dezelfde directory te plaatsen als PHPDelicious en dan patch -p0 -i fix_default_cache_path.patch te typen op je terminal.

UPDATE: ik bedacht me dat wij intern eigenlijk nog een extra patch gebruiken. Als er geen verbinding gemaakt kan worden met del.icio.us (zoals in de trein kennelijk.. duurde even voordat ik dat door had) is een duidelijke foutmelding wel handig. Hiervoor heb ik deze patch gemaakt.

Als de curl_exec() faalt, krijg je nu een PHP_DELICIOUS_ERR_CONNECTION_FAILED te zien.

UPDATE 2: inmiddels heb ik een email gehad van de auteur, waarin staat dat de CACHE_PATH-patch zal mee worden genomen in de nieuwe versie van PHPDelicious.

Nieuwe Wordpress plugin: Title Defaults to Date

Sunday, December 14th, 2008

Voor een nieuwe experimentje heb ik een lokale Wordpress blog geïnstalleerd. Deze blog ga ik schrijven in een soort dagboek-vorm, wat betekent dat de titels eigenlijk altijd gewoon simpelweg de datum en tijd van vandaag zullen zijn.

Aangezien ik niet de eerste persoon ben die Wordpress gebruikt alsof het een dagboek is, heb ik gezocht naar een plugin waarmee ik automatisch de huidige datum als titel kon gebruiken. Enige verbazing was dan ook wel aanwezig toen ik opmerkte dat deze functionaliteit wel gewenst was door andere gebruikers, maar nog niet geïmplementeerd.

En toen was Title Defaults to Date geboren. Deze plugin zet de titel automatisch naar de huidige datum als er geen titel is ingevuld.

Het datum formaat is gemakkelijk in te stellen in de plugin, via de instellingen onder ’settings’ -> ‘Title Defaults to Date’.

Als deze instelling niet wordt ingevuld wordt het datumformaat van Wordress gebruikt (in te stellen onder ’settings’ -> ‘general’).

Veel doet deze plugin dus niet, maar het doet wel genoeg om aan mijn wensen te voldoen. Hij is vrij te gebruiken onder de Creative Commons 3.0 license en te downloaden op de scripts-pagina.

Feedback is altijd welkom op martijn@digicted.nl of in de comments onder deze post.

Shared items van Google Reader automatisch naar del.icio.us sturen

Monday, January 14th, 2008

De shared items feature van Google Reader is de laatste tijd veel in het nieuws geweest. En hoewel niet alle verhalen positief waren, zijn er nog steeds genoeg mensen die dankbaar gebruik maken van de shared items.

Zelf ben ik er nooit echt mee aan de slag geweest, toen ik er gisteren naar keek zag ik dat ik in totaal twee items geshared heb. Dit was waarschijnlijk meer om te testen, dan dat ik het echt nuttig vond.

Meestal bookmark ik links gewoon naar del.icio.us, waar ze ook voor iedereen beschikbaar zijn.

Ook @Polledemaagt en @frankmeeuwsen delen hun items via del.icio.us, maar Polle gaf zaterdag op Twitter aan op zoek te zijn naar een methode om zijn shared items in Google Reader naar del.icio.us te krijgen.

Mijn eerste idee was het gebruik van een Greasemonkey script, maar dit betekent nog steeds handmatig werk - het maakt het proces hooguit iets gemakkelijker.

Daarom besloot ik om een eigen script te schrijven met de RSS-feed van Google Reader shared items en de API van del.icio.us, het resultaat: gr2dic.

Het principe is simpel: je vult je Google Shared items feed URL in (zie deze screenshot), je del.icio.us-username en je del.icio.us-password. Vervolgens worden al je shared items elke dag om 9 uur naar del.icio.us gepost en automatisch getagged met “gr2dic”.

De source code zal ik waarschijnlijk binnenkort beschikbaar maken in een andere post. Wel wil ik alvast een patch delen die ik geschreven heb voor PhpDelicious: addpost_and_deletepost_fix.patch. Zonder deze patch zullen addPost() en deletePost() altijd false returnen en een notice geven.

De auteur van PhpDelicious heb ik ook op de hoogte gesteld van deze patch, dus wellicht zal het binnenkort ook goed zijn in de officiële release.

Inmiddels een email terug van de developer, hij gaat de patch toepassen en opnieuw uploaden.

Update: Op lifehacking.nl schreef Frank meeuwsen al over mijn “oude oplossing” in het artikel Google Reader en del.icio.us Samenvoegen. De methode hierboven beschreven is dus een handigere methode dan op LH.nl gepost is.

Update 2: Er zijn vandaag wat problemen geweest met het script, maar die zouden inmiddels opgelost moeten zijn.

Wordpress Plugin: Bulk Tag Renamer

Sunday, December 9th, 2007

Naar aanleiding van een gesprek met een vriendin heb ik mijn eerste Wordpress-plugin geschreven.

Het was haar opgevallen dat het niet mogelijk is de naam van een tag te veranderen. Bijvoorbeeld alles wat getagged is met “appel” wijzigen naar “apple”.

Omdat ik al langer een Wordpress-plugin wilde schrijven en deze plugin niet al te moeilijk leek (dat viel nog wat tegen, omdat ik eerst moest leren hoe Wordpress-plugins werken) nam ik de taak op mij. Het resultaat is een plugin waarmee je snel de naam van een tag kunt wijzigen en nog een klein beetje andere functionaliteit.

Aangezien ik er een voorstander van ben om dingen één ding te laten doen, en dat ene ding dan gelijk goed, wordt het geheel mogelijk nog gesplitst naar twee plugins.

De huidige feature-list:

  • Tag hernoemen
  • “Slug” (permalink) wel of niet updaten
  • Categorieën met dezelfde naam wel of niet updaten
  • Bestaande tags aanklikken en zo in het “tagveld” zetten bij post schrijven

Installatie is simpel:

  1. Extract het zipbestand
  2. Upload de map naar wp-content/plugins/
  3. Activeer de plugin via het plugins-menu in de admin

Dat is alles! Vervolgens kun je het een link vinden in het menu manage.

Download de plugin.

Problemen met RSS-feed

Tuesday, September 25th, 2007

Gisteren ontdekte ik dat er een probleem was met de link van de RSS-feed en sommige mensen nog steeds een oude versie in de pagina kregen. Dit is nu opgelost en dat was ook gelijk te zien aan het aantal subscribers op de feed in Feedburner. Excuses voor mensen die hier last van ondervonden hebben.

Tellen in SQL of PHP: wat is sneller?

Tuesday, August 21st, 2007

Afgelopen zondag plaatste Nick een blogpost over MySQL en de fouten die hij mensen hier mee ziet maken.

In de reacties kwam er toen even een discussie of je COUNT() in je query moest gebruiken of een mysql_num_rows in je PHP-code als je het aantal rijen wilt weten.

Ik heb toen even een heel simpel scriptje gedraaid om gewoon snel te checken of mijn stelling klopte en inderdaad: COUNT() leek sneller te zijn.

Gisteren heb ik toen met een database met 1 miljoen rijen (testdata) een paar keer een script gedraaid en de resultaten opgeslagen in een database. Het testsysteem is een Intel PentiumD 930 met 2GB geheugen. PHP 5 en MySQL 5, draaiend op Windows Vista.

Eerst draaide ik het script op de MacBook Pro (wat voor de nodige load zorgde), maar toen bedacht ik me dat de PHP dan werd uitgevoerd op de laptop, terwijl ik de MySQL-server op de Windows-PC draaide. Totaal geen eerlijke vergelijking dus, dus verplaatst.

Het script haalt 1.000.000 rijen op uit een tabel met twee velden: id en name. ID is de primary key met een auto_increment en name is een tinytext-veld met willekeurige strings van willekeurige lengte. Na het ophalen worden de rijen geteld.

De resultaten (in seconden) zijn uiteindelijk als volgt:

PHP SQL
5866.500 503.085
1813.99 1237.07
917.166 490.60
476.952 442.147
562.155 459.487

Gemiddeld duurt het dus 626.47846069336 seconden om het aantal rijen te tellen in MySQL. In PHP duurt het gemiddeld 1927.3516174316 seconden.

De snelste run in PHP duurde 476.952 seconden, de snelste in SQL duurde slechts 442.147 seconden.

De traagste was voor PHP 5866.5 seconden en voor MySQL 1237.07 seconden.

Uit deze getallen kunnen we concluderen dat een COUNT() in MySQL sneller is dan mysql_num_rows() in PHP.

Echter zijn er nadelen verbonden aan het gebruik van COUNT(). Zo heb je waarschijnlijk meer code nodig wat niet altijd even goed is voor de leesbaarheid. Ook in gevallen dat je mogelijk geen rijen terugkrijgt kun je dit vaak niet gebruiken.

Is je applicatie echt zo performance-kritisch en heb je een grote dataset, kan dit dus iets zijn om naar te kijken. Maar in het algemeen zou ik gewoon mysql_num_rows (of een dergelijk alternatief) aanraden.

Ten slotte: voor de geïnteresseerden is alles wat ik gebruikt hebt gewoon beschikbaar. De source van mijn benchmarkscript (met een kleine wijziging naar aanleiding van een tip van Daan) en de SQL voor de tabel met de statistieken De dataset wil ik op aanvraag sturen naar mensen die de SQL-file willen hebben.

Ruby: backups maken van je del.icio.us bookmarks

Sunday, July 29th, 2007

Net las ik “Backup delicious to gmail” op Foobr. Aaron Bassett beschrijft hier een handige manier om een backup te maken van je del.icio.us-bookmarks door de XML-file te emailen naar je GMail-account door middel van een PHP-script.

Dit is naar mijn mening een prima manier om een backup te maken van je del.icio.us-bookmarks, maar ik gebruik liever Ruby (al was het maar omdat ik het taaltje nog steeds een beetje aan het leren ben, maar geweldig vind voor dergelijke problemen) en ik wil het niet in GMail hebben maar op de lokale harddisk. Eventueel een backup op deze website.

Eerst heb ik even gespiekt in zijn code en toen ben ik zelf begonnen.

Eerst maar even de hele code:

#!/usr/bin/env ruby
require 'net/https'

delicous_username = ""
delicous_password = ""
recent = true #only fetch recent posts? useful for debugging
xmlfile = "/fullpath/to/xmlfile"

https = Net::HTTP.new( 'api.del.icio.us', 443 )
https.use_ssl = true

https.start do |http|
  req = Net::HTTP::Get.new('/v1/posts/' + ( recent ? "recent" : "all" ) )
  req.basic_auth delicous_username, delicous_password
  xmlData = ""
  insertXslLine = true

  https.request( req ).read_body.each do |line|
    xmlData += line
    # add the reference to the xsl-file for some nice HTML-formatting
    xmlData += "<?xml-stylesheet type=\"text/xsl\" href=\"delicious.xsl\"?>\\n" if insertXslLine
    insertXslLine = false
  end
  File.new( xmlfile, "w+" ).write( xmlData )
end

De eerste paar regels zijn vooral voor de configuratie. Vul hier je del.icio.us-gebruikersnaam en -wachtwoord in, of je alleen de meest recente bookmarks wilt (zodat je niet de hele XML-file hoeft te fetchen) en de locatie voor het XML-bestand.

Dan maken we verbinding met api.del.icio.us via HTTPS en authen met onze gebruikersnaam en wachtwoord van de service. De rest is op zich best “simpel”:

  1. XML-file binnenhalen, regel voor regel;
  2. Een verwijzing naar het XSL-bestand invoegen op de 2e regel (XSL-bestand zullen we zo zien en is optioneel);
  3. De data van de XML-file met XSL-verwijzing wegschrijven naar het gekozen bestand.

Als we dat doen hebben we een kale XML-file, dus die gaan we iets mooier maken met een beetje XSLT zodat er iets uitkomt wat voor HTML door moet gaan.

<xsl:stylesheet version = '1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
  <xsl:template match="/">
    <html>
      <head>
        <title>del.icio.us bookmarks backup</title>
      </head>
      <body>
        <h2>del.icio.us bookmarks of <xsl:value-of select="posts/@user" /></h2>
        <ol>
          <xsl:for-each select="posts/post">
            <li>
              <a>
                <xsl:attribute name="href">
                  <xsl:value-of select="@href"></xsl:value-of>
                </xsl:attribute>
                <xsl:value-of select="@description"></xsl:value-of>
              </a>
            </li>
          </xsl:for-each>
        </ol>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

De output hiervan zo iets moeten zijn als:

<html>
  <head>
    <title>del.icio.us bookmarks backup</title>
  </head>
  <body>
    <h2>del.icio.us bookmarks of martijnengler</h2>
    <ol>
      <li><a href="http://pinderkent.blogsavy.com/archives/88">Sometimes it's best to leave old software systems alone.</a></li>
      <li><a href="http://www.codinghorror.com/blog/archives/000920.html">Coding Horror: Google's Number One UI Mistake</a></li>
      <li><a href="http://blog.angrypets.com/2007/07/the-case-for-ra.html">The Case for Desktop RAID 0 - Debunking the Myths</a></li>
      <li><a href="http://www.deadharddrive.com/">My dead hard drive story</a></li>
      <li><a href="http://weblog.oomph.nl/2007/07/blogger-typepad.html">Blogger, Typepad en Wordpress. Welk platform kies je voor je weblog?</a></li>
      <li><a href="http://lifehacker.com/software/how-to/turn-mailing-lists-into-an-rss-feed-283353.php">How To: Turn mailing lists into an RSS feed - Lifehacker</a></li>
      <li><a href="http://www.silverware.nl/">Silverware</a></li>
      <li><a href="http://www.youtube.com/watch?v=Xo-rm95ZBcg">Plain White T's - Hey There Delilah</a></li>
      <li><a href="http://lifehacker.com/software/how-to/hide-files-inside-of-jpeg-images-282119.php">How To: Hide files inside of JPEG images - Lifehacker</a></li>
      <li><a href="http://www.quietbay.net/Science/astronomy/nightsky/">Learn: Identify constellations, stars, planets and how to navigate at night</a></li>
      <li><a href="http://weblogs.sqlteam.com/jeffs/archive/2006/02/10/9002.aspx">Data belongs in your tables -- not in your code</a></li>
      <li><a href="http://www.ibm.com/developerworks/web/library/wa-ltwebserv/">Lightweight Web servers</a></li>
      <li><a href="http://www.dynamicajax.com/fr/AJAX_Suggest_Tutorial-271_290_312.html">Creating a Google AJAX Suggest Like Website Search</a></li>
      <li><a href="http://www.codeandcoffee.com/2007/06/27/how-to-make-a-password-strength-meter-like-google/">How to Make a Password Strength Meter Like Google - Code and Coffee</a></li>
      <li><a href="http://www.wikihow.com/Share-Your-Obituary-With-Your-Online-Friends">How to Share Your Obituary With Your Online Friends - wikiHow</a></li>
    </ol>
  </body>
</html>

Welk platform kies je voor je weblog?

Saturday, July 28th, 2007

Sanne Roemen plaatste vandaag (technisch gezien gisteren) een interessant bericht op haar weblog: “Blogger, Typepad en Wordpress. Welk platform kies je voor je weblog?“. Nu hoor ik ook wel eens mensen die graag een eigen weblog zouden beginnen. Hoewel ik ondernemers dan graag doorverwijs naar Silverware waar een weblog ook tot de mogelijkheden behoort zijn niet al mijn bekenden ondernemers.

En aangezien Sanne altijd leuke handleidingen weeet te schrijven (eerder verwees ik al eens naar haar handleiding: abonneren op rss feeds met Bloglines.), dus waarom zelf het zware werk doen? ;)

In de reacties kwam het antwoord:

Misschien zou het net iets overzichtelijker zijn als al deze informatie in een tabel gezet zou worden? Is een idee. :-)

En de reactie van Sanne:

@Roel, thanx! Een tabel is inderdaad een idee. Eerlijk gezegd had ik geen zin meer om te html-en want in de typepad editor zit geen handig tabellending. (ben ook maar een mens :-D

Toen besloot ik dus dat ik toch een stukje zelf moest doen… De tabel maken. De meeste informatie komt direct uit het bericht van Sanne, dus alle credits naar haar voor het research daarvan - ik heb zelf maar een paar kleine dingen opgezocht.

Blogger Typepad Wordpress.com Wordpress.org
Eigen host nodig Nee Nee Nee Ja
Mogelijkheid tot statische pagina’s Nee Ja Ja Ja
Layout van je pagina De standaard templates zijn makkelijk te kiezen en als je je een beetje verdiept in de code kan je er ook vrij makkelijk wijzigingen in aanbrengen. Je werkt wel altijd binnen een stramien dus 100% vrijheid heb je nooit.. Sommige templates zijn makkelijker aan te passen dan anderen. De flexibele templates bieden een wysiwyg (what you see is what you get) interface waarmee je kleuren en letters kan aanpassen. Ook hier geldt dat je binnen een stramien werkt dus geen 100% vrijheid hebt. Je kan je template wel helemaal zelf bouwen met advanced templates, maar daar moet je toch wel behoorlijk technisch voor zijn. Keuze uit een ongeevenaarde hoeveelheid templates (hier heten ze ‘Thema’s’). De templates kunnen ook van elkaar verschillen qua functionaliteiten die ze bieden. Je hebt weinig vrijheid om ze zelf nog aan te passen, soms kan je een eigen grafische balk bovenaan plaatsen. Wordpress heeft mooiere templates dan de andere twee. Voor je zelf gehoste blog kan je kiezen uit de gratis templates die online beschikbaar zijn maar je kan er ook een helemaal zelf laten ontwerpen en bouwen (dan zit je met kosten voor een ontwerper en een programmeur). Hier heb je echter wel 100% vrijheid (rekening houdend met de ‘normale’ grenzen die internet stelt).
Plugins Beperkt aantal “widgets” Voor zover ik kan zien alleen voor statistieken, maar Movable Type (het platform waar TypePad op draait ondersteunt wel plugins)Ze bestaan wel zegt Sanne. Aantal plugins standaard ingebouwd, geen eigen plugins Een zeer ruime keuze aan plugins
Ondersteuning voor meerdere auteurs Ja Alleen met Pro Level ($149.50 per jaar) of Premium Level ($299.50) Ja Ja
Kosten Gratis Vanaf $49.50 Gratis Gratis (let op: je hebt wel hosting nodig wat niet gratis is)

Let op: een eigen domein als “digicted.nl” (dus niet digicted.wordpress.com) is niet gratis.

Silverware is niet opgenomen, omdat wij niet echt een blogservice zijn, maar het een onderdeel is van het totaalpakket. Voor de mensen die het toch graag willen weten: je hebt geen eigen host nodig, je kan een eigen logo uploaden, plugins zijn niet mogelijk, er is ondersteuning voor meerdere auteurs en een ondernemersabonnement is momenteel al beschikbaar vanaf 12 euro exclusief BTW.

Statische pagina’s zijn een beetje een verhaal apart bij ons, maar dat is buiten de scope van deze post. Kijk daarvoor naar de abonnementen van Silverware of neem contact op met mij.

Update: een oplettende lezer wees mij er op dat de prijzen niet meer klopte. Dit is waarschijnlijk misgegaan bij het terugzetten van een backup. Excuses hiervoor.

Takenlijst met “toevoegen” en “verwijderen” maken met prototype

Saturday, April 7th, 2007

Eerder deze week schreef ik al over het toevoegen van een nieuwe rij met prototype waar ik uitlegde hoe je een tabel gemakkelijk dynamisch kunt uitbreiden met behulp van wat Javascript.
Dit keer gaan we weer aan de slag met prototype, maar maken we een dynamische todolist. We willen gebruikers items laten toevoegen en verwijderen en vervolgens opslaan. Voor het gemak heb ik hier een plat tekstbestand voor gebruikt, maar het is natuurlijk ook mogelijk om het naar een database te schrijven. Laten we maar weer beginnen de HTML te schrijven:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
  "http://www.w3.org/TR/html4/strict.dtd">
<html>
  <head>
    <title>Todolist</title>
    <script type="text/javascript" src="prototype.js"></script>
    <script type="text/javascript" src="todolist.js"></script>
  </head>
  <body>
    <form action="displaylist.php" method="post" id="items">
      <fieldset>
        <legend>Items</legend>
        <ul id="todolist">

        </ul>
        <input type="submit" name="done" value="Klaar" id="done">
      </fieldset>
    </form>
    <form id="addtodo" action="" method="post">
      <fieldset>
        <legend>Nieuwe items</legend>
        <label for="item">Item</label>
        <input type="text" name="item" id="item">
        <input type="submit" name="add" value="Voeg toe" id="add">
      </fieldset>
    </form>
  </body>
</html>

Het lijkt me niet dat ik hier veel uitleg bij hoef te geven. Er komen simpelweg twee formulieren en een lijst. Het tweede formulier is voor het toevoegen van nieuwe items en het eerste formulier hebben we straks nodig om de waarden echt op te slaan.

En dan het Javascript:

function addItem()
{
  $( 'addtodo' ).onsubmit = function()
  {
    var item = $F( 'item' );
    id = id + 1;
    var newitem = '<li id="todo-' + id  + '">' + item;
    newitem += ' (<a href="#" id="delete-item-' + id + '" class="delete">verwijderen</a>)';
    newitem += '<input type="hidden" name="items[]" value="' + item + '">';
    newitem += '</li>';
    new Insertion.Bottom( "todolist", newitem );
    deleteItem();
    $('item').value = '';
    Form.Element.focus( 'item' );
    return false;
  }
}

function deleteItem()
{
  var items = document.getElementsByClassName("delete");
  for (var i=0; i < items.length; i++)
  {
    var myitem = items[i];
    items[i].onclick = function()
    {
      $( 'todo-' + this.id.substring( 12 ) ).remove();
      return false;
    }
  }
}

function init()
{
  id = 0;
  addItem();
}

Event.observe(window, 'load', init, false);

Hier zien we eerst een functie om een nieuw item toe te voegen aan de lijst (additem) als het onderste form gesubmit wordt. We voegen het item toe aan de lijst en maken daarna het vak leeg. Daarna plaatsen we de focus in het vak, zodat er gelijk een nieuw item getypt kan worden.
De return false; is om te voorkomen dat het formulier echt gesubmit wordt.

Daarna deleteItem() om ook items te kunnen verwijderen. Eerst halen we daar alle elementen op met de class “delete” en daarna kijken we of er op geklikt wordt. Als dit zo is verwijderen we de complete li met het dat id van het DOM.
De andere functies zullen aardig vanzelfsprekend zijn als je het vorige artikel hebt gelezen

Nu komt de PHP:

<?php
$sList = '';
foreach( $_POST[ 'items' ] as $sItem )
{
  $sList .= "- " . $sItem . "\n\r";
}
file_put_contents( "list.txt", $sList );
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
  "http://www.w3.org/TR/html4/strict.dtd">
<html>
  <head>
    <title>Resultaat</title>
  </head>
  <body>
    <p><a href="list.txt">De lijst</a> is succesvol weggeschreven.</p>
  </body>
</html>

Ook dit stelt op zich niet zo heel veel voor:

  1. Loop door alle inputvelden heen en voeg ze toe aan een variable.
  2. Schrijf deze variable weg naar een bestand. Ik gebruik hier de functie file_put_contents, die pas beschikbaar is vanaf versie 5; als er behoefte is aan een PHP4-versie wil ik deze later nog posten.
  3. Geef de gebruiker een linkje naar zijn nieuwe todolist. Het is natuurlijk ook mogelijk om de lijst al gelijk weer te geven.

Bekijk hier de demo en stel eventuele vragen in de reacties.

Nieuwe rij toevoegen in tabel met prototype

Sunday, April 1st, 2007

Van de week kwamen we ineens een probleem tegen in de interface van Silverware. Helaas kan ik op dit moment nog niet ingaan op details (maar het zal niet erg lang meer duren voor we live gaan, beloofd! ;)), maar we moesten een mogelijkheid hebben om onbeperkt rijen toe te voegen aan een tabel.
Laten we aannemen dat de basis van de tabel ongeveer als volgt is:

<table>
  <tbody>
    <tr>
      <td>1.1</td>
      <td>1.2</td>
    </tr>
    <tr>
      <td>2.1</td>
      <td>2.2</td>
    </tr>
    <tr>
      <td>3.1</td>
      <td>3.2</td>
    </tr>
  </tbody>
</table>

Nu willen we dus onbeperkt rijen toe kunnen voegen aan deze tabel en dan het liefst zonder de pagina te verversen. Omdat ik toch al gebruik maak van Prototype heb ik besloten dat gewoon te gebruiken.
Waarschijnlijk zijn er ook andere methoden om dit op te lossen en misschien is mijn methode niet de beste (waarschijnlijk niet, ik ben niet zo’n Javascript-held eigenlijk), maar deze methode werkte goed voor mij. Eerst had ik wat problemen in Safari, maar dit is getest (en werkend bevonden) in IE6, IE7, Firefox 2, Safari 2 en Opera 9.

Om te beginnen is het natuurlijk belangrijk dat je Prototype download, zelf heb ik gebruik gemaakt van versie 1.5.0 en dat werkte prima.

De volgende stap is onze eigen javascript, wat er zo uit kwam te zien:

function addField()
{
  url = 'addrow.php';
  $( 'add-new-row' ).onclick = function()
  {
    rows = $( 'mybody' ).getElementsByTagName( 'tr' );
    id = rows.length + 1;
    doRequest( url, id, 'mybody' );
  }
}

function doRequest( url, id, type )
{
  pars = 'id=' + id +  '&type=' + type;
  addTo = type;
  var myAjax = new Ajax.Request(
                            			url,
                            			{
                            				method: 'get',
                                    parameters: pars,
                            				onComplete: showResponse
                            			});
}

function showResponse(originalRequest)
{
  new Insertion.Bottom(addTo, originalRequest.responseText );
}

Event.observe(window, 'load', addField, false);

De functie addField() wordt aangeroepen als er geklikt wordt op de link die we straks in de HTML zullen zien. Hij telt alle rijen in de tabel, telt er één bij op en je hebt een nieuw id. Misschien niet altijd wat je wilt, maar voor het voorbeeld goed denk ik.

De functie doRequest accepteert 3 parameters: url (dit is het path naar ons PHPscript), id (dit is het nieuwe id) en type (het id van ons tbody). Vervolgens bouwt het een URL op die er uiteindelijk ongeveer zo uit zou zien: phpscript.php?id=onsid&type=onstype.

Als het PHPscript de output heeft teruggestuurd, wordt showResponse() aangeroepen die een nieuwe rij toevoegt aan het einde van de tbody.

Eigenlijk is de echte versie iets uitgebreider, maar dit is denk ik het stukje wat er toe doet voor dit artikel.
Nu de HTML (uiteraard ook aardig gestripped):

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
  "http://www.w3.org/TR/html4/strict.dtd">
<html>
  <head>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8">
    <title>Dynamisch rijen toevoegen</title>
    <script type="text/javascript" src="prototype.js"></script>
    <script type="text/javascript" src="addrow.js"></script>
  </head>
  <body>
    <table>
      <tbody id="mybody">
        <tr>
          <td>1.1</td>
          <td>1.2</td>
        </tr>
        <tr>
          <td>2.1</td>
          <td>2.2</td>
        </tr>
        <tr>
          <td>3.1</td>
          <td>3.2</td>
        </tr>
      </tbody>
    </table>
    <p><a href="#" id="add-new-row">Nog een rij</a></p>
  </body>
</html>

En dan ten slotte het PHP-script:

  <?if( ctype_digit( $_GET[ 'id' ] ) ):?>
    <tr>
      <td><?=$_GET[ 'id' ]?>.1</td>
      <td><?=$_GET[ 'id' ]?>.2</td>
    </tr>
  <?endif?>

Heel kort, maar effectief. Eerst checken we even of het ID wel een nummer is (in de praktijk wil je misschien nog meer security checks doen) en vervolgens voegen we een rij toe met 2 cellen. Uiteraard heb ik even een demo online gezet om te zien dat het ook werkt in de praktijk.

Voor geïnteresseerden is de volledige code ook te downloaden.

Nog één belangrijke tip: het is heel belangrijk dat je de rij toevoegt aan je <tbody /> en niet aan je <table />. Het heeft mij behoorlijk wat tijd gekost om dat te ontdekken…