M06UF4Pr1: Receptes de cuina

De wikijoan
Salta a la navegació Salta a la cerca

Introducció

Anem a fer receptes de cuina, amb la informació de les receptes codificada amb llenguatge XML. El format i els tags que farem servir és molt simple:

<recipe>
  <title>Bean And Ham Soup</title>
  <recipeinfo>
    <blurb>A great way to use leftover holiday ham.</blurb>
    <genre>Soup</genre>
    <author>David Horton</author>
    <yield>10 servings</yield>
    <preptime>Five or six hours</preptime>
  </recipeinfo>
  <ingredientlist>
    <ingredient>1 lb. dry navy beans</ingredient>
    <ingredient>medium onion, diced</ingredient>
    <ingredient>2 cloves of garlic, minced</ingredient>
    <ingredient>2 ribs of celery, chopped</ingredient>
    <ingredient>3 carrots sliced</ingredient>
    <ingredient>3 tomatoes, diced</ingredient>
    <ingredient>1 lb cooked ham scraps, chopped</ingredient>
    <ingredient>6 cups of water</ingredient>
    <ingredient>1 ham bone</ingredient>
    <ingredient>Salt and pepper to taste</ingredient>
  </ingredientlist>

  <preparation>Rinse the beans. Combine dry beans and 10 C of water in a 5-quart stockpot and bring to a boil.\n\nRemove from heat and let soak for two hours before continuing with the recipe. Use a strainer or colander to drain the water from the beans and set aside.\n\nIn the stockpot combine ham scraps, garlic, onion, celery and carrot. Sweat vegetables and ham over medium-low heat. There should be enough fat in the ham scraps to keep the veggies from sticking to the pan, but if not add a little vegetable oil.\n\nOnce the onion becomes translucent add the tomatoes and cook down for a few minutes. When tomatoes are soft add 6 C. of water and stir to create a vegetable broth. Drain and rinse the beans. Add beans and ham bone to the vegetable broth. Simmer over low heat until beans are tender, about 2 to 3 hours.\n\nStir occasionally. After beans are tender add salt and pepper to taste. Do not add salt before this as it can toughen the beans.\n\nDiscard ham bone and skim off any oil that may be on the surface before serving.</preparation>
  <serving>Serve with rye rolls and apple slices on the side.</serving>
</recipe>

Podem anomenar aquest format RecipeXML. Un cop s'escriuen les receptes amb aquest format es pot exportar la informació a d'altres formats (HTML, PDF, Rich Text,...), com ja vas veure l'any passat a l'assignatura de XML.

Guardem la informació xml en un fitxer amb extensió xml, i generem també una imatge de 200x200 px que sigui representativa de la recepta.

Fixem-nos que els paràgrafs i retorns de carro els fem amb el caràcter \n, doncs no hem de pressuposar que la renderització del fitxer serà en una pàgina web.

Desenvolupament

Parsejar XML en el cantó del client

L'objectiu és carregar el contingut de la recepta i parsejar el contingut XML.

El fitxer index1.html carrega en una select box 2 fitxers XML (fitxers d'exemple que pots descarregar de l'enllaç). Mitjançant la tècnica asíncrona d'AJAX mostra els ingredients per pantalla, en la capa corresponent. Aquest codi és bàsicament similar al que s'ha vist a classe en l'exemple de tutoria (catàleg de CDs). fitxer index1.html:

<html>
<head>
<title>Receptes AJAX</title>
<script>
function loadXMLDoc(url)
{

var xmlhttp;
var txt,x,xx,i;

xmlhttp=new XMLHttpRequest();

xmlhttp.onreadystatechange=function()
  {
  if (xmlhttp.readyState==4 && xmlhttp.status==200)
    {
    txt="<h2>Ingredients</h2>";
    txt = txt + "<ul>";
		x=xmlhttp.responseXML.documentElement.getElementsByTagName("ingredient");
		for (i=0;i<x.length;i++) {
			txt = txt + '<li>' + x[i].firstChild.nodeValue + '</li>';
		}
    txt = txt + "</ul>";
    document.getElementById('info').innerHTML=txt;
    }
  }
xmlhttp.open("GET",url,true);
xmlhttp.send();

}
</script>
</head>
<body>
<h1>Receptes</h1>
<div id="formulari">
<form name="frm_receptes" id="formu" action="#">
	<select name="recipes" onchange="loadXMLDoc(this.value)">
	<option value="recipes/bean-and-ham.xml">bean and ham</option>
	<option value="recipes/corn-chowder.xml">corn chowder</option>
	</select><br />
</form>
</div>
<div id="info">
</div>
</body>
</html>

Obtenim:

2 Tbsp. butter
medium onion, diced
2 cloves garlic, minced
2 ribs celery, chopped
1 red pepper, diced
4 ears of corn kernels cut from the cob
2 Tbsp. flour
2 C. chicken broth
2 C. whole milk
3 medium red potatoes, cubed
Salt and pepper to taste

Fixa't bé que estem parsejant el fitxer XML en el cantó del client (Javascript). L'objecte xmlhttp.responseXML conté tot els fitxer complet, i amb Javascript filtrem la informació que ens interessa, en aquest cas els ingredients. Això seria ineficient si el fitxer XML fos molt gros (posem per cas 500K) i només ens interessa una petita part de la informació.

L'alternativa és que sigui PHP, en el cantó del servidor, qui parsegi el fitxer XML i només envïi al client la informació que interessa, en aquest cas els ingredients

Parsejar XML en el cantó del servidor

Ara és PHP qui s'encarrega de parsejar el XML. La informació que rebem no és tot el fitxer XML, sinó només allò que ens interessa, com ara la llista d'ingredients. La funcionalitat PHP de parsejar XML forma part del core, no cal instal·lar res.

script index2.html:

<html>
<head>
<title>Receptes AJAX</title>
<script>
function loadXMLDoc(recepta) {

  let xmlhttp=new XMLHttpRequest();

  xmlhttp.onreadystatechange=function() {
    if (xmlhttp.readyState==4 && xmlhttp.status==200) {
      //alert(xmlhttp.responseText);
      //console.log(xmlhttp.responseText);
      document.getElementById('info').innerHTML=xmlhttp.responseText;
    } else {
      document.getElementById('info').innerHTML="<img src=\"img/ajax_wait.gif\" />";
    }
  }
  
  xmlhttp.open("GET","processar_ingredients.php?recepta="+recepta,true);
  xmlhttp.send();
}
</script>
</head>
<body>
<h1>Receptes</h1>
<div id="formulari">
<form name="frm_receptes" id="formu" action="#">
  <select name="recipes" onchange="loadXMLDoc(this.value)">
  <option value="recipes/bean-and-ham.xml">bean and ham</option>
  <option value="recipes/corn-chowder.xml">corn chowder</option>
  </select><br />
</form>
</div>
<div id="info">
</div>
</body>
</html>

script processar_ingredients.php:

<?php
$fitxerxml = $_GET["recepta"];
echo "<h1>Ingredients $fitxerxml</h1>";
$xml = simplexml_load_file($fitxerxml);
//echo $xml->getName() . "<br>";

//per bolcar tota la informació:
//print_r($xml);

echo "<ul>";
foreach($xml->ingredientlist->ingredient as $ingredient){
	echo "<li>";
	echo $ingredient;
	echo "</li>";
}
echo "</ul>";
sleep(1);
?> 

Per tal de simular una comunicació molt lenta o una transacció de molta informació (icona ajax_wait.gif), hem introduït sleep(1) en el codi PHP, i en el codi javascript introduïm:

if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
   document.getElementById('info').innerHTML=xmlhttp.responseText;
} else {
   document.getElementById('info').innerHTML="<img src=\"img/ajax_wait.gif\" />";
}

La idea és que abans d'arribar a l'estat 4 (xmlhttp.readyState==4) passem pels estats 1, 2 i 3, i aquests són els moments on podem mostrar la icona del wait típica del AJAX.

En aquest cas el que retorna el PHP és un echo o print. Però en d'altres ocasions el que ens interessarà és que retorni un objecte JSON.


creat per Joan Quintana Compte, gener 2021