You are here:  » Search and Sort function


Search and Sort function

Submitted by rubenxela on Mon, 2010-01-25 15:17 in

Hello. I'm realy new to xml and realy bad with PHP !!! And .... not so good to speak english !

I'm trying (for a while)to parse a xml feed and for to use many functions. I started with many articles on the web and after coding a good part, I finaly changed my method and bought MagicParser which is so good for my needs. But as a newbie there is many things I'm trying to do without success !
Maybe I'll come back with other topics for special things but here I would like to ask your help for my search and sort function !

I can't find the way to search into a xml file using MagicParser !! And I'm now with my first line of code that I wrote before using MagicParser.

The code I use and that I need a kind of conversion using MagicParser is :

My Research.php file

<?php
$rss 
'xmlfile.xml';
$xml = @simplexml_load_file($rss); 
// CONDITIONS
 
$research $xml->XPath('/Backslash/bien[mprixeuro<='.$_GET['price'].'][mprixeuro>='.$_GET['price2'].'][lcp="'.$_GET['search'].'"]' );
foreach (
$research as $monproduit) { 
    foreach (
$monproduit->biendesc as $produit)   { 
$pays utf8_decode($produit->clangue);
// JUST CHECK FRENCH LANGUAGE ITEMS
if ($pays==fr) {  
// PARSE
echo '<a href="article.php?num='.utf8_decode($monproduit->nbien).'"><img src="'.utf8_decode($monproduit->sfilenamephoto1).'" border="0" style="float:left; margin-right:20px"></a>';
echo 
'<p style="text-align: justify">'.utf8_decode($produit->lregion).'';
echo 
'</p>';
echo 
'<strong>'.utf8_decode($produit->ltitre).'</strong>';
echo 
'<br />';
echo 
'<small>'.utf8_decode($produit->lannonce).'</small> ';
echo 
'<br />';
echo 
'<small>'.utf8_decode($monproduit->mprixeuro).' euros</small> ';
}
}
}
?>

This code works OK for my need. Search is like : searchinto XML file, items with zip code(lcp)=XX and price between XX and XXX euros.
But I want to :
- Use MagicParser to do the same thing
- Sort the items by Price (mprixeuro)
- And maybe using a pagination system (what I do with other pages using MParser), but I don't know if it's possible with Sorting !!

My XML File is like that : (This is 1 item)

<Backslash>

<bien>
<nbien>100066</nbien>
<cagence>03gildc</cagence>
<lusername>ifergal</lusername>
<cbien>FG0801</cbien>
<mprixeuro>39600</mprixeuro>
<mprix>39600</mprix>
<ctyp>20</ctyp>
<ndept>46</ndept>
<nannee>1960</nannee>
<qsurfterrain>80</qsurfterrain>
<cetat>-</cetat>
<qchambres>3</qchambres>
<qpieces>4</qpieces>
<qsurfhab>80</qsurfhab>
<qsurfsejour>28</qsurfsejour>
<cregion>46210</cregion>
<nprospectpropr>20071</nprospectpropr>
<cenvironnement>brg</cenvironnement>
<ctypmandat>v</ctypmandat>
<lcp>46210</lcp>
<llocalite>LATRONQUIERE</llocalite>
<cisopays>FRA</cisopays>
<nnatmandat>2</nnatmandat>
<cnaturebien>res</cnaturebien>
<netageappart>2</netageappart>
<netagetot>2</netagetot>
<lvue>S</lvue>
<qsdd>1</qsdd>
<qgarages>1</qgarages>
<cchauf>ELEC</cchauf>
<ntypcuisine>6</ntypcuisine>
<mrevcad/>
<mcharge/>
<mprecimmo>550</mprecimmo>
<cstyle>T</cstyle>
<dcre>2008-11-03</dcre>
<dmod>2009-10-19</dmod>
<mcommission>3600</mcommission>
<ctypmeta>appt</ctypmeta>
<bactif>1</bactif>
<sfilenamephoto1>03gildc100066p21596.jpg</sfilenamephoto1>

<photo>
<nphoto>21596</nphoto>
<lshortdescfr>Q480801A</lshortdescfr>
<sfilename>03gildc100066p21596.jpg</sfilename>
<nwidth>454</nwidth>
<nheight>303</nheight>
<nangle/>
<nordre>1</nordre>
</photo>

<photo>
<nphoto>21591</nphoto>
<lshortdescfr>Q480801B</lshortdescfr>
<sfilename>03gildc100066p21591.jpg</sfilename>
<nwidth>454</nwidth>
<nheight>303</nheight>
<nangle/>
<nordre>2</nordre>
</photo>

<photo>
<nphoto>21592</nphoto>
<lshortdescfr>Q480801C</lshortdescfr>
<sfilename>03gildc100066p21592.jpg</sfilename>
<nwidth>454</nwidth>
<nheight>303</nheight>
<nangle/>
<nordre>3</nordre>
</photo>

<photo>
<nphoto>21593</nphoto>
<lshortdescfr>Q480801D</lshortdescfr>
<sfilename>03gildc100066p21593.jpg</sfilename>
<nwidth>454</nwidth>
<nheight>303</nheight>
<nangle/>
<nordre>4</nordre>
</photo>

<photo>
<nphoto>21595</nphoto>
<lshortdescfr>Q480801F</lshortdescfr>
<sfilename>03gildc100066p21595.jpg</sfilename>
<nwidth>454</nwidth>
<nheight>303</nheight>
<nangle/>
<nordre>5</nordre>
</photo>

<photo>
<nphoto>21590</nphoto>
<lshortdescfr>Q480801G</lshortdescfr>
<sfilename>03gildc100066p21590.jpg</sfilename>
<nwidth>454</nwidth>
<nheight>303</nheight>
<nangle/>
<nordre>6</nordre>
</photo>

<biendesc>
<clangue>fr</clangue>
<ltypmeta>Appartements</ltypmeta>
<lregion>Région LA TRONQUIERE</lregion>
<lnaturebien>Résidentiel</lnaturebien>
<lenvironnement>Bourg</lenvironnement>
<lstyle>Traditionnel</lstyle>
<ltypmandat>Biens AV</ltypmandat>
<letat>A rafraîchir</letat>
<lchauf>Electricité</lchauf>
<ltypcuisine>Aménagée</ltypcuisine>
<ltyp>Appartement</ltyp>
<ltitre>Appartement de 80 m²</ltitre>

<lannonce>
Dans petit bourg sympathique du Ségala avec tous commerces, Appartement de 80 m² de type T4 au 2ème étage d'une petite résidence à taille humaine avec garage et cave. Jardin de 80 m². CC avec chaudière électrique. A rafraîchir.
</lannonce>

<lannonceweb>
Dans petit bourg sympathique du Ségala avec tous commerces, Appartement de 80 m² de type T4 au 2ème étage d'une petite résidence à taille humaine avec garage et cave. Jardin de 80 m². CC avec chaudière électrique. A rafraîchir.
</lannonceweb>

<lhtml>
<P style="MARGIN-TOP: 3pt; MARGIN-BOTTOM: 7pt">Dans petit bourg sympathique du Ségala avec tous commerces, Appartement de 80 m² de type T4 au 2ème étage d'une petite résidence à taille humaine avec garage et cave. Jardin de 80 m². CC avec chaudière électrique. A rafraîchir.</P><P style="MARGIN-TOP: 3pt; MARGIN-BOTTOM: 7pt"></P><P style="MARGIN-TOP: 3pt; MARGIN-BOTTOM: 7pt">&nbsp;</P><P align=justify><SPAN>&nbsp;</SPAN></P>
</lhtml>

<lascii>
Dans petit bourg sympathique du Ségala avec tous commerces, Appartement de 80 m² de type T4 au 2ème étage d'une petite résidence à taille humaine avec garage et cave. Jardin de 80 m². CC avec chaudière électrique. A rafraîchir.
</lascii>
</biendesc>

<biendesc>
<clangue>nl</clangue>
<ltypmeta>Appartements</ltypmeta>
<lregion>Région LATRONQUIERE</lregion>
<lnaturebien>Residentieel</lnaturebien>
<ltypmandat>Verkoop</ltypmandat>
<letat>Normaal</letat>
<lchauf>Electriciteit</lchauf>
</biendesc>

<biendesc>
<clangue>de</clangue>
</biendesc>

<biendesc>
<clangue>en</clangue>
<ltypmeta>Apartments</ltypmeta>
<lregion>LATRONQUIERE AREA</lregion>
<lnaturebien>Residential</lnaturebien>
<lenvironnement>Town</lenvironnement>
<lstyle>Other</lstyle>
<ltypmandat>Property for Sale</ltypmandat>
<letat>Needs decorating</letat>
<lchauf>Electricity</lchauf>
<ltypcuisine>Fitted</ltypcuisine>
<ltyp>Appartement</ltyp>
</biendesc>
</bien>

Submitted by support on Mon, 2010-01-25 15:37

Hi there,

As Magic Parser is a serial parser (it reads each record in turn and then
passes it to your myRecordHandler function), what you need to do is test
the fields according to your search parameters at the top of myRecordHandler,
and then only process the record if the conditions meet your search requirements.

Here's a basic example without any of the display / handling code, which you can
simply copy in from your current Magic Parser test scripts (i've just used print_r
as example code here)....

<?php
  
require("MagicParser.php");
  function 
myRecordHandler($record)
  {
    
// ignore record if does not meet criteria
    
if (
       (
$record["MPRIXEURO"]<=$_GET['price'])
       ||
       (
$record["MPRIXEURO"]>=$_GET['price2'])
       ||
       (
$record["LCP"]<>$_GET['search'])
       )
       return;
    
// use record here
    
print_r($record);
  }
  
MagicParser_parse("xmlfile.xml","myRecordHandler","xml|BACKSLASH/BIEN/");
?>

That's the basic way to ignore records that you are not interested in.

Sorting is slightly more complex, as what you need to do is load the records
into a global array; then sort the global array, and finally display the
sorted array.

A very nice PHP function to do this is usort(). This function lets you sort
an array by comparing items with a user specified function that can make the
order decision based on arbitrary PHP code.

Extending the example above to only display (again using print_r) records
which meet your criteria, but then sorted by price (mprixeuro) you would
do something like this:

<?php
  
require("MagicParser.php");
  
// global array to hold records we are interested in
  
$records = array();
  function 
myRecordHandler($record)
  {
    global 
$records;
    
// ignore record if does not meet criteria
    
if (
       (
$record["MPRIXEURO"]<=$_GET['price'])
       ||
       (
$record["MPRIXEURO"]>=$_GET['price2'])
       ||
       (
$record["LCP"]<>$_GET['search'])
       )
       return;
    
// use record here
    // as this records meets the criteria add to global array
    
$records[] = $record;
  }
  
MagicParser_parse("xmlfile.xml","myRecordHandler","xml|BACKSLASH/BIEN/");
  
// here we are after the parse we can sort $records by price
  
function cmp($a$b)
  {
    if (
$a["MPRIXEURO"] == $b["MPRIXEURO"]) {
      return 
0;
    }
    return (
$a["MPRIXEURO"] < $b["MPRIXEURO"]) ? -1;
  }
  
usort($records,"cmp");
  
// now display or use the sorted records
  
foreach($records as $record)
  {
    
print_r($record);
  }
?>

I appreciate that's quite a lot that will be new if you are new to PHP
so let me know if you're not sure about anything, but I hope this points
you in the right direction!

Cheers,
David.

Submitted by rubenxela on Mon, 2010-01-25 15:56

Thank you very much David. I'll try it right now and let you know.

Submitted by rubenxela on Mon, 2010-01-25 16:35

Very nice !! it works with few lines of code.

So now another point is coming to my mind :
Is it possible to write conditions like

<?php
if (
       (
$record["MPRIXEURO"]<=$_GET['price'])
       ||
       (
$record["MPRIXEURO"]>=$_GET['price2'])
       ||
       (
$record["LCP"]<>$_GET['search'])
       )
?>

Doing like if that conditions are optionals. I mean if for exemple search criteria is empty, it will search only for other disponible criterias ?
I can imagine a way to do it using else if and explaining all posiblities ... but maybe ..... ??

Submitted by support on Mon, 2010-01-25 16:49

Hi,

The most straight forward way is to test each item individually, for example:

<?php
if ($_GET['price']) { if ($record["MPRIXEURO"]<=$_GET['price']) return; }
if (
$_GET['price2']) { if ($record["MPRIXEURO"]>=$_GET['price2']) return; }
if (
$_GET['search']) { if ($record["LCP"]<>$_GET['search']) return; }
?>

The net result is a logical OR, so you can just add as many tests as you want
like this...

Hope this helps!
Cheers,
David.

Submitted by rubenxela on Mon, 2010-01-25 21:19

I'm grateful for the help you've given to me. Yet there is one last thing I can't find the solution : the paging system. I tried with the piece of code related to other thread on the forum but failed after many attempts.

Submitted by support on Tue, 2010-01-26 09:54

Hi,

I've merged a very basic Previous/Next page navigation into the last example
above - have a go with something like this;

<?php
  
require("MagicParser.php");
  
// global array to hold records we are interested in
  
$records = array();
  if(!
$_GET['page'])$page 1// If there is a current page number, use it.
  
else $page $_GET['page']; //If there is no page number, set one!
  
$total_number_of_items 0//Count the total number of items in the xml feed
  
$items_per_page 10//Set the number of items displayed per page
  
$counter 0;  
  function 
myRecordHandler($record)
  {
    global 
$records;
    
// ignore record if does not meet criteria
        
if ($_GET['price']) { if ($record["MPRIXEURO"]<=$_GET['price']) return; }
        if (
$_GET['price2']) { if ($record["MPRIXEURO"]>=$_GET['price2']) return; }
        if (
$_GET['search']) { if ($record["LCP"]<>$_GET['search']) return; }
    
// use record here
    // as this records meets the criteria add to global array
    
$records[] = $record;
    
// increment total number of records for use in navigation
    
$total_number_of_items++;
  }
  
MagicParser_parse("xmlfile.xml","myRecordHandler","xml|BACKSLASH/BIEN/");
  
// here we are after the parse we can sort $records by price
  
function cmp($a$b)
  {
    if (
$a["MPRIXEURO"] == $b["MPRIXEURO"]) {
      return 
0;
    }
    return (
$a["MPRIXEURO"] < $b["MPRIXEURO"]) ? -1;
  }
  
usort($records,"cmp");
  
// now display or use the sorted records according to $page
  
foreach($records as $record)
  {
    
$counter++;
    
// return false whilst parsing items on previous pages
    
if ($counter <= (($page-1)*$items_per_page)) continue;
    
print_r($record);
    
// exit loop if reached maximum to display on page
    
if ($counter == ($page $items_per_page)) break;
  }
  
// finally create navigation bars
  
$total_pages_count ceil($total_number_of_items/$items_per_page);
  
$page_href "?price=".urlencode($_GET['price'])."&price2=".urlencode($_GET['price2'])."&search=".urlencode($_GET['search']);
  if (
$page 1)
  {
    print 
"<a href='".$page_href."&page=".($page-1)."'>&laquo; Previous</a>";
  }
  else
  {
    print 
"&laquo; Previous";
  }
  print 
" | ";
  if (
$page $total_pages_count)
  {
    print 
"<a href='".$page_href."&page=".($page+1)."'>Next &raquo;</a>";
  }
  else
  {
    print 
"Next &raquo;";
  }
?>

If you're not sure about anything just let me know!

All the best,
David.

Submitted by rubenxela on Tue, 2010-01-26 14:44

Thank you David. This code display 10 items and everything works with my variables. But I found a problem with the code ??) and I was with the same thinking when I tried the pagination system for the first time for more basic use with another piece of code from the forum.

At line 7 $total_number_of_items is defined as being equal to zero

$total_number_of_items = 0;

And everywhere I try something like

<?php
 
echo $total_number_of_items 
?>

or
<?php
 
echo $total_pages_count
?>

It's always sending a zero !!

When I try also the other piece of code from the foum with "page 1" "page 2" etc ... it send a message error "Warning: Division by zero in /homez.95/xxx/www/xxxxx/rub.php on line 61"

And the result is that the "Next" link never end.

Submitted by support on Tue, 2010-01-26 14:47

Hi,

Sorry - it's because $total_number_of_items is not declared as global within
the myRecordHandler function. It should be:

  function myRecordHandler($record)
  {
    global $records;
    global $total_number_of_items;
    // ignore record if does not meet criteria
        if ($_GET['price']) { if ($record["MPRIXEURO"]<=$_GET['price']) return; }
        if ($_GET['price2']) { if ($record["MPRIXEURO"]>=$_GET['price2']) return; }
        if ($_GET['search']) { if ($record["LCP"]<>$_GET['search']) return; }
    // use record here
    // as this records meets the criteria add to global array
    $records[] = $record;
    // increment total number of records for use in navigation
    $total_number_of_items++;
  }

That should be all it is...

Cheers!
David.

Submitted by rubenxela on Tue, 2010-01-26 15:29

Thank you David, it works find.

Finaly for paging system I did something like pages 1 - 2 - 3 - 4 .... and used the QUERY_STRING to build the paging links, because of many variables possible.

I write the piece of code here for the paging system with numbering, because I saw that many people look for it.

<?php
// finally create navigation bars
  
$total_pages_count ceil($total_number_of_items/$items_per_page);
  unset(
$_GET['page']);
  
$page_href =   http_build_query($_GET);
 echo 
"Total number of items ".$total_number_of_items." <br />" 
for(
$i=1$i<=$total_pages_count$i++)
{
     if(
$i==$page)
     {
         echo 
' [ '.$i.' ] ';
     }
     else
     {
          echo 
' <a href="?'.$page_href.'&page='.$i.'">'.$i.'</a> ';
     }
}
?>

Submitted by rubenxela on Thu, 2010-01-28 13:42

I've another question for a function that I need for this same page .
I want to use this type of variable like you explain to me before

<?php
 
if ($_GET['price']) { if ($record["MPRIXEURO"]<=$_GET['price']) return; }
        if (
$_GET['price2']) { if ($record["MPRIXEURO"]>=$_GET['price2']) return; }
        if (
$_GET['search']) { if ($record["LCP"]<>$_GET['search']) return; }
?>

But I need for one field to make a search in Title field to find if the keyword $_GET['q'] is found in titles. Something like the operator LIKE for sql. But with the operators == <> etc ... I can't find a solution to do this kind of search
<?php
 
if ($_GET['q']) { if ($record["BIENDESC/LTITLE"]??????$_GET['q']) return; }
?>

Submitted by support on Thu, 2010-01-28 13:48

Hi,

You can use strpos(), and === FALSE as the comparison, for example:

<?php
 
if ($_GET['q']) { if (strpos($record["BIENDESC/LTITLE"],$_GET['q'])===FALSE) return; }
?>

Using "=== FALSE" (=== in PHP means "very equal to", i.e. by value _and_ type) allows you to distinguish between not found (strpos returns FALSE) and found at the very start of the string (strpos returns 0).

Hope this helps!
Cheers,
David.

Submitted by rubenxela on Thu, 2010-01-28 16:17

One more time thank you David. This is exactly what I needed. In fact I didn't use strpos() but stristr() which is Case-insensitive.