2

I asked a question last week about creating a script to search an XML feed of events information.

Using multiple Xpath queries to search XML

The question was closed with the suggestion I come back once I've written some code.

I've done that now, so I wanted to post what I'd done and ask if I've approached this in the right way.

Here are the parameters I want to search:

id - brings back the information for the specific event with the matching id attribute.

q - a general search query that will return matches anywhere in an event note

keywords, venue, ticketed, starttime - look for matches within these specific elements

program_list, performer_list - look for matches in any children of these elements (there are multiple performers in each event)

dateFrom, dateTo - allow for a date range search.

Here's a sample event:

  <event id="69399">
    <starttimeunixtime>1349791200</starttimeunixtime>
    <endtimeunixtime>1350507600</endtimeunixtime>
    <starttime>2012-10-09 15:00:00</starttime>
    <endtime>2012-10-17 22:00:00</endtime>
    <formatted_startdate>09 October 2012</formatted_startdate>
    <formatted_enddate>17 October 2012</formatted_enddate>
    <formatted_starttime>3:00pm</formatted_starttime>
    <formatted_endtime>10:00pm</formatted_endtime>
    <venue>Royal Albert Hall</venue>
    <title>A concert of Music</title>
    <performer_list>
      <performer_7>
        <performer>A.N. Other</performer>
        <instrument>Viola</instrument>
      </performer_7>
    </performer_list>
    <program_list>
      <program_8>
        <program>Beethoven</program>
        <program_work>Symphony No. 2</program_work>
      </program_8>
    </program_list>
    <description><![CDATA[<p>This is the description&nbsp;This is the descriptionThis is the descriptionThis is the descriptionThis is the descriptionThis is the descriptionThis is the descriptionThis is the descriptionThis is the descriptionThis is the descriptionThis is the descriptionThis is the descriptionThis is the descriptionThis is the descriptionThis is the descriptionThis is the descriptionThis is the descriptionThis is the descriptionThis is the descriptionThis is the descriptionThis is the descriptionThis is the description</p>]]></description>
    <shortDescription><![CDATA[<p>This is the short description&nbsp;This is the short description&nbsp;This is the short description&nbsp;This is the short description&nbsp;This is the short description&nbsp;This is the short description&nbsp;This is the short description&nbsp;This is the short description&nbsp;</p>]]></shortDescription>
    <eventType>Highlights</eventType>
    <keywords>orchestra</keywords>
    <ticketInfo><![CDATA[<p>This is the ticket info&nbsp;</p>]]></ticketInfo>
    <ticketed>YES</ticketed>
  </event>

And here's my script:

<?
//Load events XML
$events=simplexml_load_file('events.xml');

//If single event find that info
if(isset($_GET['id'])):
    $id=preg_replace("/[^0-9]/","",$_GET['id']);
    $query='@id="'.$id.'"';
    $outputtype="singleevent";

//Allow for a general search that will search all fields
elseif(isset($_GET['q'])):
    $searchterm=strtolower(preg_replace("/[^A-Za-z0-9\s:-]/","",$_GET['q']));
    $query.='contains(translate(.,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz"), "'.$searchterm.'") and ';
    echo $query;
    $query= substr_replace($query, "", -5);


//Otherwise build a query to do the search. 
elseif  (!empty($_GET)):
    foreach($_GET as $key => $value):

        //Remove unwanted characters from userinput
        $searchterm=strtolower(preg_replace("/[^A-Za-z0-9\s:-]/","",$value)); 

        //Do a simple search
        //Xpath doesn't support case insensitive searching, so I'm using the Xpath translate function, which swaps uppercase letters for their lowercase counterpart.
        if($key=='keywords' || $key=='venue' || $key=='ticketed' || $key=='starttime'):         
            $query.='contains(translate('.$key.',"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz"), "'.$searchterm.'") and ';
        endif;

        // Do a search of composite fields
        if($key=='program_list' || $key=='performer_list'):         
            $query.='contains(translate('.$key.'/*,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz"), "'.$searchterm.'") and ';
        endif;  

        // Allow for a date range query (we might have dateFrom, dateTo or both!)

        if($key=='dateFrom'):
            $query.='endtimeunixtime > '.strtotime($searchterm. "00:00:00").' and ';
        endif;

        if($key=='dateTo'):
            $query.='starttimeunixtime < '.strtotime($searchterm." 23:59:59").' and ';
        endif;          
    endforeach;

    // Remove final 'and' from query

    $query=substr_replace($query, "", -5);

//End of Query Builder
endif;

// If there is no query, then show all events.  

if($query==""):
    $query='*';
endif;

//Query XML
$events=($events->xpath('event['.$query.']'));

//Use $events to generate pages

?>

It seems to work as required, but I was just wondering if anyone had any feedback/suggestions of alternate methods as I'm going to need to do this a lot with XML in future. Thanks!

Community
  • 1
  • 1
DaveR
  • 2,355
  • 1
  • 19
  • 36
  • It's not exactly pretty, but yes, this is one way to do it. It's the procedural approach where you simply check which $_GET params are there and then concatenate the query on a param by param basis. You even made sure the params do not contain any characters that could alter the resulting XPath queries. – Gordon Oct 09 '12 at 17:01
  • 1
    How would you do it in a pretty way? – DaveR Oct 09 '12 at 17:04
  • 1
    I'd probably build some sort of specialized Query Builder for this, where each method corresponds to one query param. Something like `$eventQuery->venue('Amaryllis')->program('Beethoven')->build()` or `$eventQuery->add(new Venue('Amaryllis))->add(new Program('Beethoven))->build()`. This would capsule the logic how to sanitize the params as well as the logic to assemble it from the parts. But that's just me. And it's a lot of effort. Yours ain't bad. It's just somewhat hard to read, but really thumbs-up for figuring this out *and* coming back after we shot you down last week. Good work! – Gordon Oct 09 '12 at 17:16

0 Answers0