Separar conteúdo da Apresentação com XSLT, SimpleXML e PHP 5

Tirar proveito nos módulos PHP XSL e SimpleXML

Ao longo dos anos, desenvolvedores arquitetaram muitas estratégias e estruturas para facilitar a separação da lógica de negócios e da lógica de apresentação. Neste tutorial, você irá explorar duas soluções para separar dados e a lógica de negócios da lógica de apresentação: uma usando XSLT através do módulo XSL em PHP 5 e a outra usando o módulo SimpleXML em PHP 5. Para fazer isso, você usará uma página da Web para um currículo pessoal é armazenado como um arquivo XML como exemplo.

Jake Miles, Senior Developer, Conde Nast

Photo of Jacob MilesJake Miles é um oficial de ligação técnico senior na Twistage, Inc que trabalha atualmente com aplicativos Facebook, Myspace e OpenSocial utilizando Java, PHP, Adobe Flex e JavaScript. Ele trabalha como desenvolvedor profissional há 10 anos e é um estudante ávido e curioso desde os 10 anos. Ele também leciona como voluntário.



07/Out/2008

Antes de Iniciar

Este tutorial é para desenvolvedores de aplicativos da Web que buscam separar dados e a lógica de negócios da lógica de apresentação. Experiência em PHP, conhecimento de XML em geral e familiaridade com XSLT são todos requisitos necessários.

Sobre este Tutorial

A maioria dos aplicativos da Web pegam dados do usuário, convertem os mesmos em um formato armazenável em um banco de dados e, em seguida, pegam os dados do banco de dados e convertem os mesmo em uma página da Web. Ao criar uma página da Web a partir de dados, uma abordagem é criar uma única página PHP que age como um modelo para a página da Web, que contém todas as consultas do bancos de dados e outras lógicas necessárias para reestruturar os dados em um formulário que pode ser usado pela página. Um problema com essa abordagem, no entanto, é que mistura interesses:

  • Um interesse é do designer da página da Web, criar o layout da página
  • Outro é do desenvolvedor, extrair os dados do banco de dados
  • Um terceiro interesse é a reestruturação intermediária dos dados para que sejam usados mais facilmente na página

Separação dos interesses refere-se à extração do código para diversas camadas:

  • Uma camada para lidar com o banco de dados
  • Uma camada para manipular os dados
  • Uma camada de apresentação criando a interface com o usuário

Este tutorial demonstrará duas implementações dessa separação de interesses.

Acrônimos Usados Frequentemente

  • CSS: Cascading stylesheet
  • HTML: Linguagem de Marcação de Hipertexto
  • PHP: PHP Hypertext Preprocessor
  • XML: Linguagem de Marcação Extensível
  • XSD: XML Schema Definition
  • XSL: Extensible Stylesheet Language
  • XSLT: Transformações XSL

A separação de interesses é fundamental para código bem estruturado, pois encapsula responsabilidades e dependências. Por essa razão, ao longo dos anos, os desenvolvedores arquitetaram muitas estratégias e estruturas projetadas para facilitarem ou, até mesmo, impingirem a separação da lógica de negócios e a lógica de apresentação, já que as duas frequentemente variam independentemente uma da outra. Uma sequência de folhas de estilo XSLT torna essa separação bem clara, pois você pode dividir a conversão de dados de seu formulário persistido em uma apresentação visual em fases discretas à medida que XML é transformado por uma folha de estilo e, em seguida, a próxima. No entanto, a sintaxe e metodologia de XSLT, um paradigma declarativo, não é a norma para a maioria dos programadores que usam linguagens processuais. O módulo SimpleXML em PHP é uma resposta processual a XSLT, que permite atravessar um documento XML como um objeto PHP padrão.

Neste tutorial, você irá implementar uma página da Web para um currículo pessoal é armazenado como um arquivo XML e explorar duas soluções para separar dados e lógica de negócios da lógica de apresentação:

  • Uma usando XSLT através do módulo XSL em PHP 5
  • A outra usando o módulo SimpleXML em PHP 5

Primeiro, você irá criar um pequeno script de driver e, em seguida, implementar uma separação de conteúdo e apresentação primeiro, implementando três folhas de estilo XSLT que funcionam em seqüência e, por fim, implementando a mesma transformação em PHP usando o módulo SimpleXML. Apesar de o mecanismo fundamental subjacente a essas duas tecnologias (XSLT e PHP) ser bem diferente, o código resultante terá uma estrutura muito semelhante entre as duas implementações.

Apesar de a maioria dos dados ser armazenada em um banco de dados relacional nos dias de hoje, para um pequeno conjunto de dados que são alterados com pouca frequência e não requerem bloqueio de registro, como um currículo pessoal é, XML é um formato ideal que permite inclusões fáceis. Neste tutorial, você irá explorar XSLT e SimpleXML na renderização de um documento XML de currículo, é como uma página da Web HTML.

Pré-requisitos

Você precisará das ferramentas a seguir neste tutorial:

  • Este tutorial usa PHP versão 5.2.6.
  • SimpleXML está disponível em todas as versões de PHP 5.0 ou posterior e é ativado por padrão.
  • PHP 5 inclui a extensão XSL por padrão. Para ativá-la, pode incluir o argumento --with-xsl[=DIR] em sua linha de configuração. Para obter informações adicionais sobre como instalar e configurar a extensão XSL, consulte http://us.php.net/manual/en/xsl.setup.php.
  • Um editor de XML.

Primeiras Etapas

Para executar as transformações da folha de estilo XSLT ou as transformações baseadas em SimpleXML, você precisa de um script de driver para chamar de um navegador. Aqui você usará um único script de driver que delega para o controlador correto com base em uma variável de pedido que especifica qual técnica usar.

Criando o Script de Driver

Entre de cabeça e crie o script de driver index.php na Listagem 1.

Listagem 1. Configurando
<?php

require_once ('XsltChainController.php');
require_once ('SimpleXmlResumeController.php');

function processRequest() {
  switch ($_REQUEST['conversion']) {
    case 'xslt':
      produceResultUsingXslt();
      return;
    case 'simpleXml':
      produceResultUsingSimpleXml();
      return;
    default:
      throw new Exception ("unrecognized 'conversion' type (request parameter): " .
$_REQUEST['conversion']);
  }
}

function produceResultUsingXslt () {

  $controller = new XsltChainController
    (array ('resume-restructured.xsl',
          'resume-restructured-to-view.xsl',
        'resume-view-to-html.xsl'));

  $controller->echoTransformedXmlFile ('resume.xml');
}


function produceResultUsingSimpleXml () {

  $controller = new SimpleXmlResumeController();

  $controller->renderResumeView ('resume.xml');
}

processRequest();

?>

Use um momento para revisar alguns realces do script na Listagem 1. Observe como processRequest() usa um parâmetro de consulta conversion . E ProduceResultUsingXslt() cria uma instância de XsltChainController, passando uma array de folhas de estilo para aplicar em sucessão e, em seguida, chama echoTransformedXmlFile() , especificando o nome do arquivo XML a transformar e renderizar para a saída padrão para exibição no navegador. ProduceResultUsingSimpleXml() deixa a lógica para um SimpleXmlResumeController, pedindo que renderize uma visualização para o arquivo XML fornecido.

A Mecânica de Direcionar Transformações de XSLT em PHP 5

Para decretar as transformações da folha de estilo XSLT, crie a classe XsltChainController como na Listagem 2.

Listagem 2. XsltChainController.php
<?php

class XsltChainController {

  private $stylesheets;

  public function __construct ($stylesheets) {
    $this->stylesheets = $stylesheets;
  }

  public function echoTransformedXmlFile ($xmlFile, $echoAsHtml=true) {
    $xmlDOM = DOMDocument::load ($xmlFile);
    foreach ($this->stylesheets as $stylesheet) {
      $xmlDOM = $this->applyStylesheet ($xmlDOM, $stylesheet);
    }
    echo $echoAsHtml ? $xmlDOM->saveHTML() : $xmlDOM->saveXML();
  }

  protected function applyStylesheet ($xmlDOM, $xslFile) {

    $processor = new XSLTProcessor();
    if (! $processor->hasExsltSupport()) {
      die ('no exslt support');
    }

    $xslDOM = DOMDocument::load ($xslFile);

    $processor->importStyleSheet($xslDOM);

    return $processor->transformToDoc($xmlDOM);
  }
}

?>

Essa classe é um processador genérico simples para uma string de folhas de estilo XSLT. Seu construtor pega as folhas de estilo para aplicar em ordem como nomes de arquivos, usando echoTransformedXmlFile(), aplicando a primeira ao arquivo XML especificado, passando o resultado de cada transformação de folha de estilo (um DOMDocument) para o próximo aplicativo de folha de estilo e emitindo eco da saída final. Observa a chamada para saveHTML() de DOMDocument. Se a tag raiz do XML resultante da transformação de uma folha de estilo for <html>, XSLTProcessor insere, por padrão, uma meta tag como na Listagem 3.

Listagem 3. Meta Tag Interrompida Inserida por XSLTProcessor quando a Saída Começa por <html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

Observe que essa tag não é XML bem formado, pois termina com um caractere maior que (>) em vez de os caracteres barra e maior que (/>). Isso está bem se você aplicar uma única folha de estilo em um arquivo XML para produzir HTML, mas, nesse caso, interromperá DOMDocument quando o último tentar analisar a mesma para o próximo aplicativo de folha de estilo na string. A solução é chamar XSLTProcessor::transformToDoc() em vez de transformToXML() no XSLTProcessor, para produzir um DOMDocument em vez de uma string XML e, em seguida, chamar saveHTML() no DOMDocument quando a última folha de estilo tiver produzido o resultado HTML.

Você pode ver como applyStylesheet() demonstra a mecânica de aplicar uma folha de estilo em um documento XML nesse caso. Primeiro, você cria um DOMDocument que representa o XML, que você pode criar a partir de um arquivo usando DOMDocument::load(). Em seguida, você chama importStylesheet() com um DOMDocument da própria folha de estilo XSLT para carregar o processador XSLT com a folha de estilo e, em seguida, chama transformToDoc() com o DOMDocument do XML a transformar.

O Currículo XML é

Agora que você possui um processador de script de driver e de string XSLT, verifique o arquivo XML de origem, mostrado na Listagem 4. O arquivo XML completo está disponível no código de origem.

Listagem 4. Arquivo XML de Origem
<resume>

  <person>
    <name>Jake Miles</name>
    <title>Senior software developer, freelance writer</title>
    <email>jacob.miles@gmail.com</email>
    <url>http://jakemiles.com</url>
    <summary>
      Senior Java/J2EE/Web developer.  Excellent design/development skills.
      Clear graphic interface design.  Excellent writing, communication and
      leadership skills.
    </summary>
  </person>

  <skills>
    <skillset name="Web">
      Flex, Actionscript 3, LAMP (Linux/Apache/MySql/PHP)
    </skillset>
    <skillset name="Java">
      Spring, Hibernate
    </skillset>
    <skillset name="Other">
      Scheme, Common Lisp, XSLT/XQuery/Xpath
   </skillset>
  </skills>

  <orgs>
    <org id="ibm-dev-works" display="IBM developerWorks">
      <display>IBM developerWworks</display>
      <title>Freelance writer</title>
      <link>http://www.ibm.com/developerworks/</link>
    </org>
    <org id="lit-partners" display="Literacy Partners">
      <display>Literacy Partners</display>
      <location>New York, NY</location>
      <title>Group tutor (teacher)</title>
      <link>http://www.literacypartners.org</link>
    </org>
  </orgs>

  <act-types>
    <act-type id="pub" display="Publications"/>
    <act-type id="vol" display="Volunteer"/>
    <act-type id="gig" display="Freelance"/>
    <act-type id="work" display="Work Experience"/>
  </act-types>

  <acts>

    <act type="pub"
      org="ibm-dev-works">
      <title>
    <link>http://www.ibm.com/developerworks/edu/r-dw-r-face3.html</link>
    <display>Three-part tutorial series: Mastering Facebook application
development</display>
      </title>
      <description>
    Three-part tutorial series on implementing a Facebook application
    from scratch using Java, PHP 5, IBM WebSphere, IBM DB2,
    Zend Core For IBM, Apache 2, the JSON JSP tag library,
    and the Facebook Java and PHP client libraries. 
      </description>
    </act>

<act type="vol"
      org="lit-partners">
      <description>
    Teach an evening math class once a week to students working towards
    their GED. 
      </description>
    </act>

</acts>

</resume>

O currículo é contém uma tag <person> que descreve o proprietário do currículo, é uma tag <skills> que contém os conjuntos de qualificações da pessoa, uma tag <orgs> que contém as organizações ou empresas onde a pessoa trabalhou e uma seção <act-types> que contém objetos <act-type>, sendo que cada um representa um tipo de ato ou realização que alguém pode ter em um currículo é. A seção <acts> contém as realizações da pessoa, cada uma das quais contém uma referência de ID para um dos objetos <org> e um dos objetos <act-type>. Os objetos <org> e <act-type> contêm um atributo id e display, permitindo que os objetos <act> façam referência a eles por ID; os atributos display especificam seus nomes de exibição.

Essa estrutura não-normalizada é realista e semelhante a o que você pode armazenar em um banco de dados para as mesmas informações, mas muito mais fácil de manter para tal conjunto de dados pequeno que não é alterado frequentemente e cujas mudanças são praticamente inclusões exclusivas ao longo do tempo. Você simplesmente inclui novos elementos <act> à medida que realiza coisas na vida e um novo elemento <org>, se necessário. Não há necessidade de compor instruções SQL nem criar uma interface com o usuário e você pode até mesmo armazenar o currículo é em um sistema de controle de versão.


Implementando as Folhas de Estilo XSLT

Agora que você possui um currículo é de dados em formato XML, a meta é transformar os dados em uma página HTML com um belo estilo. Isso requer duas etapas distintas:

  • Uma para reorganizar os próprios dados, agrupando-os e classificando-os conforme desejado
  • Outra para converter os dados reestruturados em uma página HTML

Neste tutorial, você irá executar essa etapa ainda mais e incluir uma terceira folha de estilo na string. A terceira folha de estilo define modelos customizados específicos da visualização que remover por refatoração estruturas HTML repetidas da segunda folha de estilo, criando uma camada de abstração sobre o HTML específico para o layout da página. Você verá mais sobre isso posteriormente ao criar a terceira folha de estilo.

A Primeira Folha de Estilo: Reestruturando os Dados

A primeira etapa é reorganizar os dados de origem. Crie uma folha de estilo XSTL, resume-restructured.xsl, como na Listagem 5.

Listagem 5. O resume-restructured.xsl Estrutural
<xsl:stylesheet version="1.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:key name="acts-by-type" match="//act" use="@type"/>

</xsl:stylesheet>

Todas as folhas de estilo XSLT 1.0 começam com a tag <xsl:stylesheet>, conforme mostrado na Listagem 5, definindo o namespace do esquema e a versão XSTL (o módulo XSL de PHP 5 suporta a versão 1.0).

A folha de estilo define uma chave XSLT, que são hashtables de valores de chaves para conjuntos de nós XML. A primeira define uma chave chamada acts-by-type, que corresponde a todos os elementos <act> documento (a consulta XPath //act significa corresponder a todos os elementos <act> na raiz ou abaixo dela em qualquer profundidade) e agrupar elementos <act> por seu atributo type . Isso significa que uma chamada a key('acts-by-type', '1') em outra parte da folha de estilo retornar;a um conjunto de nós que contém todos os elementos <act> cujos atributos type são iguais a 1 — uma referência ao elemento <act-type> cujo atributo id é 1, apesar de isso não estar definido em nenhum lugar explicitamente na folha de estilo (você poderia definir esse relacionamento explicitamente usando um esquema XSD). Chaves são úteis na melhora do desempenho e também são críticas em uma técnica de agrupamento chamada Meunchian Grouping (consulte Recursos para obter um link).

Em seguida, inclua os modelos que correspondem aos nós do documento de origem, começando com um modelo que corresponde à raiz como na Listagem 6.

Listagem 6. Modelo XSLT Correspondente à Tag <resume> no Nível-raiz (resume-restructured.xsl)
  <xsl:template match="/resume">
    <resume>
      <xsl:copy-of select="person"/>
      <xsl:apply-templates select="skills"/>
      <xsl:apply-templates select="//act-type"/>
    </resume>
  </xsl:template>

Isso corresponderá ao elemento <resume> de nível superior e emite a saída de uma tag <resume> como um literal; essa é uma técnica para copiar um nó enquanto também processa seus filhos. Na tag <resume> criada, esse modelo usa <xsl:copy-of> para copiar o elemento <person> (um filho direto de <resume>) literalmente, em seguida, chama <xsl:apply-templates> no elemento filho <skills> de <resume> e, em seguida, novamente em todos os elementos <act-type> do documento.

Isso o leva ao modelo do elemento <skills>, na Listagem 7.

Listagem 7. Modelo XSLT Correspondente à Tag <skills> (resume-restructured.xsl)
  <xsl:template match="skills">
    <skills>
      <xsl:copy-of select="skillset[@name='Java']"/>
      <xsl:copy-of select="skillset[@name='Web']"/>
      <xsl:copy-of select="skillset[@name='Other']"/>
    </skills>
  </xsl:template>

Esse modelo funciona para ordenar os elementos <skillset> no currículo é, que, teoricamente, podem estar em qualquer ordem e não necessariamente a ordem desejada de exibição na página da Web. Semelhante ao modelo do elemento <resume>, em resposta ao nó <skills> recebido, esse modelo emite saída de uma nova tag <skills> como um literal (você também pode emitir saída de um novo elemento usando <xsl:element>, mas usar o literal é muito mais simples e geralmente suficiente). Na tag <skills> criada, copia três elementos <skillset> inteiramente usando <xsl:copy-of>. A expressão XPath skillset[@name='Java'] seleciona qualquer filho direto do nó atual (o elemento <skills> correspondido) cujo nome do elemento é <skillset> e cujo atributo name é igual a Java. Isso retorna um conjunto de nós, mas, neste caso, retorna somente um elemento, pois o atributo name do <skillset> também é (implicitamente) o ID exclusivo do <skillset> entre os conjuntos de qualificações.

Os modelos, até o momento, lidaram com dados um tanto quanto estáticos no documento de origem, de forma que o modelos de qualificações pode ordenar os conjuntos de classificações explicitamente por nome. O próximo modelo, para <act-type> (consulte a Listagem 8), realiza o grosso do trabalho da folha de estilo, à medida que agrupa e ordena os elementos <act> que representam as realizações do currículo individual da pessoaé .

Listagem 8. Modelo XSLT que Corresponde à Tag <act-type> (resume-restructured.xsl)
<xsl:template match="//act-type">

    <xsl:element name="act-type">

      <xsl:attribute name="id"><xsl:value-of
select="@id"/></xsl:attribute>
      <xsl:attribute name="display"><xsl:value-of
select="@display"/></xsl:attribute>

      <xsl:variable name="type" select="@id"/>

      <xsl:variable name="actsForType" select="key('acts-by-type', $type)"/>

      <xsl:for-each select="//org">
    <xsl:variable name="org" select="@id"/>

    <xsl:if test="count($actsForType[@org = $org]) > 0">
      <act-org-in-type>
        <xsl:copy-of select="."/>
        <xsl:copy-of select="$actsForType [@org = $org]"/>
      </act-org-in-type>
    </xsl:if>

      </xsl:for-each>

    </xsl:element>
  </xsl:template>

Esse modelo corresponde à consulta de XPath //act-type, o que significa que é qualquer elemento denominado <act-type> no documento. Lembre-se de que o elemento <act-type> define um tipo de <act> que pode aparecer no documento e possui somente um atributo id e display . A meta desse modelo é reestruturar os <act>s no documento de origem, de forma que sejam agrupados por tipo e, em seguida, por organização em cada tipo, pois é assim que aparecerão na página de Web —cada tipo tendo sua própria seção visual com uma borda em torno e cada organização listada em negrito no tipo, com todos os atos realizados para essa organização desse tipo listados abaixo da mesma.

Primeiro, esse modelo recria o elemento <act-type> correspondido na saída. Aqui, ele cria o elemento <act-type> usando <xsl:element> e <xsl:attribute>, explicitamente, definindo-os como nós na árvore, em vez de literais no texto de saída. O elemento <act-type> de saída difere do correspondido de forma que conterá elementos <act-org-in-type>, cada um contendo um elemento <org> e aqueles elementos <act> que representam a interseção dessa organização e do tipo entre os <act>s no currículo é.

O modelo cria uma variável actsForType que chama a chave acts-by-type com o ID do <act-type> atual para obter todos os elementos <act> correspondentes ao <act-type> atual. Em seguida, seleciona todos os elementos <org> no documento com a expressão XPath //org e efetua loop por eles usando <xsl:for-each>, armazenando o valor @id do atual em uma variável $org para fazer referência a ele algumas linhas depois em uma expressão XPath enquanto retém o escopo correto. O elemento <xsl:if> indica que se a variável $actsForType contiver algum elemento <act> com um valor @org que corresponda ao <org> atual de <xsl:for-each>, emite saída de uma tag <act-org-in-type>, contendo:

  • Uma cópia do elemento <org>
  • Todos os elementos <act> correspondentes a esse <org> que estão ma variável $actsForType

O resultado é que <act>s são reorganizados no documento resultante, agrupados por tipo e por organização em cada tipo.

A Segunda Folha de Estilo XSLT: Renderizando a Visualização

Agora que os dados estão reestruturados para refletirem como devem ser representado na página da Web, você pode criar uma segunda folha de estilo que converte esse currículo reestruturado é em uma árvore XML que representa a visualização — um documento que é quase, mas não exatamente, HTML. Especifica a maior parte da página HTML, mas usa tags customizadas para especificar determinadas estruturas de visualização de forma mais sucinta e deixa a conversão das mesmas em HTML para uma terceira folha de estilo.

A folha de estilo começa como a primeira, correspondendo com relação ao elemento <resume> do nível-raiz, mas esse <resume> é o recriado na primeira folga de estilo, não a tag <resume> original do resume.xml. O modelo de nível superior define a página HTML geral como na Listagem 9.

Listagem 9. Modelo XSLT Correspondente à Tag <resume> no Nível-raiz (resume-restructured-to-view.xsl)
<xsl:stylesheet version="1.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/resume">
    <html>
      <head>
    <title>Resume: <xsl:value-of select="person/name"/></title>
    <css-link>resume.css</css-link>
      </head>
      <body>
    <xsl:apply-templates select="person" mode="header"/>
    <xsl:apply-templates select="skills"/>
    <xsl:apply-templates select="act-type"/>
      </body>
    </html>
  </xsl:template>

</xsl:stylesheet>

O modelo emite a saída dos elementos <head> e <body> da página e no elemento <body> processa o elemento <person>, o elemento <skills> e os elementos <act-type>, todos descendentes diretos do elemento <resume> de nível superior, refletindo o currículo reestruturado é como saída pelo resume-restructured.xsl.

Observe que esse modelo também contém um elemento não-HTML, <css-link>. Esse é um exemplo de um elemento que será convertido em HTML na terceira folha de estilo e um exemplo de definir sua própria linguagem de renderização de visualização sobre HTML. A tag <css-link> é menos detalhada do que a tag <link> padrão para uma folha de estilo CSS e a terceira folha de estilo aplicada posteriormente converterá-la na tag <link> padrão.

O próximo modelo renderiza informações sobre a pessoa no currículo é, como na Listagem 10.

Listagem 10. Modelo XSLT Correspondente à Tag <person> no Modo de Cabeçalho (resume-restructured-to-view.xsl)
  <xsl:template match="person" mode="header">

    <h1 id="name"><xsl:value-of select="name"/></h1>
    <h2><xsl:value-of select="title"/></h2>

    <header-link><xsl:value-of select="email"/></header-link>
    <header-link><xsl:value-of select="url"/></header-link>

    <section title="Summary">
      <xsl:value-of select="summary"/>
    </section>

  </xsl:template>

Para finalidades de demonstração, esse modelo usa um modo, que permite que você corresponda com relação à mesma expressão XPath em diferentes contextos. Por exemplo, você também pode ter um modelo definido como <xsl:template select="person" mode="details">, que corresponde com relação aos mesmos elementos que o modelo de outra pessoa, mas somente se o modelo que estiver chamando <xsl:apply-templates> especificar um modo de detalhes. Nesse sentido, especificar um modo em um modelo é exatamente como definir uma função em uma linguagem como PHP. Se você olhar novamente a Listagem 9 para o modelo <resume> de nível superior, você verá que chama <xsl:apply-templates select="person" mode="header"> e especifica que se diversos modelos correspondem aos nós fornecidos, chamam somente aquele que especifica um modo de cabeçalho — exatamente como chamar uma função chamada header() com o conjunto de elementos correspondente.

Esse modelo usa outra tag de visualização customizada (como <css-link> e <header-link> nos modelos acima), chamada <section>. A combinação de HTML/CSS para produzir o resultado visual do currículo é é complicada o suficiente que vale a pena refatorar seu próprio modelo na terceira folha de estilo. Isso também permite alterar como <sections> são inteiramente renderizadas, permitindo que você brinque com o design visual da página sem modificar e, potencialmente, interromper a lógica de negócios que estrutura os dados e converte os mesmos em construções de visualização de alto nível, como <section>.

Em seguida, inclua um modelo para criar a seção "Tech Skills" da página como na Listagem 11.

Listagem 11. Modelo XSLT Correspondente à Tag <skills> (resume-restructured-to-view.xsl)
  <xsl:template match="skills">
    <section title="Tech Skills">
      <xsl:apply-templates select="skillset"/>
    </section>
  </xsl:template>

Aqui, novamente, você usa a tag <sections> para criar uma seção no currículo renderizado é, adiando sua representação HTML real para a terceira folha de estilo.

Listagem 12. Modelo XSLT Correspondente a uma Tag <skillset> (resume-restructured-to-view.xsl)
  <xsl:template match="skillset">
    <div class="skill-group">
      <div class="skill-group-name"><xsl:value-of select="@name"/>:</div>
      <div class="skill-group-skills">
    <xsl:value-of select="."/>
      </div>
    </div>
  </xsl:template>

Como cada <skillset> contém somente texto listando as qualificações, <xsl:value-of select="."/> emite a saída do texto no elemento <skillset>.

As próximas correspondências do modelo nos elementos <act-type> produzidos na primeira folha de estilo (versões enfeitadas daquelas no resume.xml), renderizando a maior parte do conteúdo no currículo é, como na Listagem 13.

Listagem 13. Modelo XSLT Correspondente a uma Tag <act-type> (resume-restructured-to-view.xsl)
  <xsl:template match="act-type">
    <section title="{@display}">
      <xsl:apply-templates select="act-org-in-type"/>
    </section>
  </xsl:template>

Esse modelo demonstra como transferir um valor de atributo do elemento de origem correspondente para um novo elemento produzido na saída; o título da tag <section> é configurado para o valor do atributo display no elemento <act-type>, com a sintaxe {@display}. As chaves { } interpretam seu conteúdo como uma expressão XPath. Esse modelo produz a tag <section> e, na mesma, processa os elementos <act-org-in-type> filhos gerados na folha de estilo de reestruturação, agrupando <act>s por organização no <act-type>.

O modelo na Listagem 14, processa, então, os próprios elementos <act-org-in-type>.

Listagem 14. Modelo XSLT Correspondente a uma Tag <act-type-in-org> (resume-restructured-to-view.xsl)
  <xsl:template match="act-org-in-type">
    <xsl:apply-templates select="org"/>
    <xsl:apply-templates select="act"/>
  </xsl:template>

Esse modelo corresponde aos elementos <act-org-in-type> filhos gerados dos elementos <act-type> e processa o elemento <org> filho e os elementos <act> de <act-org-in-type>. A Listagem 15 mostra os modelos dos elementos filhos.

Listagem 15. Modelos XSLT Correspondentes às Tags <org> e <act> (resume-restructured-to-view.xsl)
  <xsl:template match="org">
    <div class="experience-header">
      <div class="experience-organization">
    <span class="organization-location"><xsl:value-of
select="location"/></span>
    <span class="organization-name"><xsl:value-of
select="@display"/></span>
      </div>
      <div class="position">
    <span class="position-title"><xsl:value-of
select="title"/></span>
      </div>
    </div>
  </xsl:template>

  <xsl:template match="act">
    <div class="accomplishment">
      <xsl:value-of select="description"/>
    </div>
  </xsl:template>

Esses modelos produzem a parte principal da página: as realizações do currículo éagrupadas por organização em cada tipo de realização (freelance, assalariado, voluntário, publicação).

A Terceira Folha de Estilo: Convertendo a Visualização Híbrida em HTML

O documento convertido agora representa a visualização, mas não é exatamente HTML; ainda contém algumas tags de visualização customizadas que precisam ser renderizadas. Crie uma terceira folha de estilo, resume-view-to-html.xsl, como na Listagem 16.

Listagem 16. resume-view-to-html.xsl
<xsl:stylesheet version="1.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="//section">
    <div class="section-title">
      <span class="section-title"><xsl:value-of
select="@title"/></span>
    </div>
    <div class="section">
      <!-- need to use node() to copy descendant text nodes as well as tags -->
      <xsl:copy-of select="node()"/>
    </div>
  </xsl:template>

<xsl:template match="//css-link">
    <link rel="stylesheet" type="text/css" href="{.}"/>
  </xsl:template>

  <xsl:template match="//header-link">
    <h3><a href="mailto:{.}"><xsl:value-of
select="."/></a></h3>
  </xsl:template>

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

Os três primeiros modelos correspondem às tags de visualização customizadas <section>, <css-link> e <header-link>, todas as quais removem por refatoração o detalhamento ou a redundância do documento de visualização e colocam nessa folha de estilo, definindo uma linguagem de visualização de nível superior. Observe o uso das chaves { } para transferir valores para atributos em elementos XML renderizados, como {.}, que configura o valor dos atributos para o texto do elementos atualmente correspondido.

O último modelo nessa folha de estilo é o que permite que funcione primeiramente. Esse é o modelo de identidade XSLT, que corresponde e copia tudo do documento literalmente, exceto qualquer coisa que corresponda a um modelo que corresponda mais especificamente a folha de estilo, ou seja, aqueles para //section, //css-link e //header-link. A expressão XPath @*|node() corresponde a qualquer atributo ou nó, onde um nó é uma tag ou nó de texto. <xsl:copy>, diferentemente de <xsl:copy-of> que renderiza uma cópia profunda do elemento, copia somente a própria tag, não seus atributos ou elementos filhos. Isso permite que o modelo copie apenas a própria tag e, em seguida, chame <xsl:apply-templates> em todos os seus atributos e filhos, permitindo que eles potencialmente correspondam os outros modelos na folha de estilo.

Com essas três folhas de estilo em vigor, você pode ir para http://your-server/index.php?conversion=xslt e o navegador exibirá seu currículo XML três vezes transformadoé, renderizado como HTML. Consulte o código em anexo para a folha de estilo CSS, resume.css.

Figura 1. Exibindo o Currículo XML Transformado
Exibindo o Currículo XML Transformado

Implementando a Transformação em PHP

Agora que você renderizou o currículoé usando XSLT através do módulo PHP XSL, você pode recriar o esforço usando PHP através do módulo SimpleXML para comparar as duas tecnologias. A primeira vista, PHP pode parecer radicalmente diferente de XSLT, mas, na verdade, o código PHP pode ser estruturado e considerado surpreendentemente semelhante às folhas de estilo XSLT. A diferença é que XSLT foi projetado para uma cascata de conversões, facilitando a separação da renderização em três transformações todas implementadas usando os mesmos métodos e sintaxe. Fazer isso em PHP é menos direto. Apesar de páginas PHP serem, elas mesmas, um excelente mecanismo para separar lógica de conteúdo da lógica de apresentação, o veículo natural para passar informações de uma etapa da conversão para a próxima é uma árvore de objetos PHP em oposição a uma árvore XML renderizada.

O Controlador

Para começar, crie um controlador análogo a XsltChainController, para direcionar o processo de renderização, como na Listagem 17.

Listagem 17. SimpleXmlResumeController.php
<?php

class SimpleXmlResumeController {

  public function renderResumeView ($xmlFile) {

    $resume = simplexml_load_file ($xmlFile);

    include ('resume-restructured.php');
    include ('resume-restructured-to-view.php');
    include ('resume-view-to-html.php');

    renderView (new ResumeRestructured ($resume));
  }
}

?>

SimpleXmlResumeController pega o arquivo XML para renderizar e cria um SimpleXMLElement a partir dele com uma chamada para simplexml_load_file(). Em seguida, inclui três arquivos PHP e usa duas coisas fornecidas por essas páginas:

  • Uma classe ResumeRestructured
  • Uma função renderView pertencente a nenhuma classe

Projetar a inclusão do código dessa forma permite separar as preocupações de forma idêntica ao design das folhas de estilo. resume-restructured.php define uma classe ResumeRestructured que se constrói como uma versão reestruturada do SimpleXMLElement com o qual é fornecida. Resume-restructured-to-view.php define a página de saída exatamente como resume-structured-to-view.xsl fez e resume-view-to-html.php fornece funções que definem a renderização view-to-HTML. O truque é que resume-structured-to-view.php chama essas funções enquanto renderiza sua saída, sem estar ciente o que elas produzem no final. O resultado é que você ainda pode descarregar para a área de troca as definições dessas funções para renderizar HTML como desejam, como pode com a terceira folha de estilo, pois o comportamento de renderização HTML é definido como funções PHP simples que são disponibilizadas na hora em que renderView() é chamado (que reside na segunda folha de estilo, resume-structured-to-view.php). O efeito de como é possível estruturar seu código é idêntico às folhas de estilo XSLT.

Reestruturando os Dados em PHP

A primeira etapa nesse processo, como era do lado do XSLT, é reestruturar os dados do currículo é para exibir, como na Listagem 18.

Listagem 18. resume-restructured.php
<?php

class ResumeRestructured {

  public function __construct ($resume) {

    $this->person = $resume->person;

    $this->skillsets = $this->doSkillsets ($resume);

    $this->actTypes = $this->doActTypes
($resume->{'act-types'}->{'act-type'},
                     $resume->acts->act,
                     $resume->orgs->org);
  }

}
?>

A classe ResumeRestructured será o objeto de dados resultante dessa fase de transformação. $resume é o SimpleXMLElement criado a partir de resume.xml. ResumeRestructured transfere a propriedade $person , um SimpleXMLElement, do objeto de dados de origem para ele mesmo, em seguida, converte os conjuntos de qualificações e os tipos de atos da origem $resume, exatamente como a primeira folha de estilo XSLT fez.

A chamada para doActTypes() demonstra a ideia básica por trás de SimpleXML. Um SimpleXMLElement representa os elementos filhos do elemento XML subjacente como propriedades PHP nele mesmo, de forma que $resume->person retorne um SimpleXMLElement que representa o elemento <person> filho do elemento <resume>.

A chamada para doActTypes() refere-se a $resume->acts->act. Como $resume->acts contém muitos elementos <act> como filhos, SimpleXMLElement disponibiliza os mesmos em uma propriedade array chamada act. Portanto, $resume->acts->act é uma array de SimpleXMLElements, cada um representando um dos elementos <act> filhos do elemento <acts> — simplesmente coloca os mesmos em uma propriedade de array cujo nome é seu nome de elemento.

A chamada para doActTypes() também demonstra como acessar um SimpleXMLElement filho que representa um elemento XML cujo nome contém um hífen ou outros caracteres que são válidos em um nome de tag XML, mas que não são válidos em um nome de propriedade PHP. $resume->{'act-types'} acessa uma propriedade PHP chamada act-types, que, quando colocada entre chaves { } é um nome de propriedade PHP válido.

O construtor também chama doSkillsets() para converter os elementos <skillset> no currículo é, como na Listagem 19. Se você consultar novamente o resume-structured.xsl, verá que essas funções refletem uma estrutura semelhante a seus modelos XSLT correspondentes.

Listagem 19. function doSkillsets() (resume-restructured.php)
  protected function doSkillsets ($resume) {
    return (array (first ($resume->xpath("//skillset[@name = 'Java']")),
           first ($resume->xpath("//skillset[@name='Web']")),
           first ($resume->xpath("//skillset[@name='Other']"))));
  }

Essa função retorna os conjuntos de qualificações ordenados conforme desejado, como o modelo correspondente fez na folha de estilo XSLT. First() é uma função auxiliar (consulte o código fonte anexado) que retorna apenas o primeiro elemento de uma array fornecida. DoSkillsets() demonstra o uso de XPath para selecionar em uma árvore SimpleXMLElement. Aqui as expressões XPath para selecionarem cada conjunto de qualificações em ordem são praticamente idênticas àquelas do modelo //skillset no resume-structured.xsl, exceto que você deve especificá-las como caminhos absolutos —SimpleXMLElement->xpath() requer que as expressões XPath sejam absolutas.

Em seguida, como na folha de estilo XSLT, a próxima etapa é reestruturar os tipos de atos, como na Listagem 20.

Listagem 20. function doActTypes() (resume-restructured.php)
  protected function doActTypes ($actTypes, $acts, $orgs) {

    $orgsById = $this->mapNodesToAttribute ('id', $orgs);

    $actsByType = $this->groupNodesByAttribute ('type', $acts);

    $actsByOrg = $this->groupNodesByAttribute ('org', $acts);

    $newActTypes = array();
    foreach ($actTypes as $actType) {
      $actTypeId = (string) $actType['id'];

      $newActType = new Object();
      $newActType->id = $actTypeId;
      $newActType->display = (string) $actType['display'];

      $newActType->orgsInType =
     (! array_key_exists($actTypeId, $actsByType) ? array() :
      $this->createActOrgsInType ($actsByType[$actTypeId],
                      $actsByOrg,
                      $orgsById));

      $newActTypes[] = $newActType;
    }

    return $newActTypes;
  }

DoActTypes() efetua loop pela array fornecida de $actTypes, como SimpleXMLElements, e constrói a substituição de objetos act-type no objeto resultante. DoActTypes() copia, então, os atributos id e display dos tipos de atos de origem e inclui a propriedade orgsInType que contém as combinações de org/act de forma análoga de como a folha de estilo XSLT reagrupou as mesmas. Para simplificar, transfira o act-type SimpleXMLElements para o objeto resultante e, em seguida, enfeite-os com a array filho orgsInType que produziu o erro "Aviso: Ainda não é possível designar tipos complexos para propriedade" a partir de SimpleXMLElement. A solução é criar uma cópia do tipo de ato e copiar seus atributos, como você fez com XSLT.

Observe a necessidade de efetuar cast de uma string em $actTypeId = (string) $actType['id'];. Nem SimpleXMLElements nem atributos neles são strings, apesar de quando passados para echo() ou para o operador de concatenação de string PHP (.), terem cast implicitamente efetuado para representações de string.

GroupNodesByAttribute () retorna uma array associativa de arrays, cada uma chaveada para um valor exclusivo de um atributo especificado nos SimpleXMLElements fornecidos (consulte a Listagem 21).

Listagem 21. Função groupNodesByAttribute() (resume-restructured.php)
  protected function groupNodesByAttribute ($attribute, $items) {
    $groups = array();
    foreach ($items as $item) {
      $key = (string) $item [$attribute];
      if (! array_key_exists($key, $groups)) {
    $groups [$key] = array();
      }
      $groups[$key][] = $item;
    }
    return $groups;
  }

groupNodesByAttribute() agrupa-os por esse atributo e facilita consultar todos os atos com um determinado tipo ou valor org. Isso simula uma chave XSLT. MapNodesToAttribute(), usado acima (consulte a Listagem 21) em doActTypes(), é semelhante, retornando uma array associativa dos SimpleXMLElements fornecidos, cada um mapeado para seu valor do atributo fornecido. Isso funciona somente porque cada org tem um ID exclusivo e, portanto, somente um org é associado com o valor do atributo.

Cada um dos objetos act-type criados no objeto resultante contém um ou mais actOrgsInType, criados por createActOrgsInType() (consulte a Listagem 22.)

Listagem 22. Função createActOrgsInType() (resume-restructured.php)
  protected function createActOrgsInType ($actsForType, $actsByOrg, $orgsById) {
    $orgsInType = array();
    foreach ($orgsById as $orgId => $org) {

      $acts = $this->selectActsWithOrgId ($orgId, $actsForType);

      if (count($acts) > 0) {
    $orgInType = new Object();
    $orgInType->org = $org;
    $orgInType->acts = $acts;
    $orgsInType[] = $orgInType;
      }
    }
    return $orgsInType;
  }

Isso conclui o processo de agrupamento, para consultar todos os atos com um determinado ID de org e, se existir algum, criar um objeto orgInType a ser incluído na propriedade da array actOrgsInType do act-type.

Esse processo demonstra como é semelhante converter entre estruturas de dados em XSLT e PHP, apesar de a primeira usar uma sintaxe declarativa e a segunda uma processual.

Renderizando a Visualização em PHP

Após reestruturar os dados, você está pronto para renderizar a visualização, usando as mesmas abstrações feitas nas folhas de estilo, mas implementando-as de forma ligeiramente diferente, como na Listagem 23.

Listagem 23. resume-restructured-to-view.php
<?php

function renderView($resume) { ?>

<html>
<head>
    <title>Resume: <?= $resume->person->name; ?></title>
    <? cssLink('resume.css'); ?>
</head>
<body>
<?
    doPerson ($resume->person);
    doTechSkills ($resume->skillsets);
    foreach ($resume->actTypes as $actType) {
      doActType ($actType);
    }
?>
  </body>
</html>

<? 
} 

?>

Aqui o elemento css-link é criado através de uma chamada a uma função cssLink() , que não está definida nessa página, removendo-a por abstração e tonando-a trocável pela versão desejada. A listagem para doPerson() demonstra isso ainda mais (consulte a Listagem 24).

Listagem 24. function doPerson (resume-restructured-to-view.php)
function doPerson ($person) { ?>

  <h1 id="name"> <?= $person->name ?></h1>
  <h2><?= $person->title ?></h2>

  <? headerLink ($person->email); ?>
  <? headerLink ($person->url); ?>

  <? startSection ("Summary"); ?>

  <?= $person->summary ?>

  <? endSection(); ?>

<?
}

Aqui os elementos de link de cabeçalho e de seção são todos renderizados através de funções definidas em outro local, removendo-os, então, por fatoração desse estágio da transformação apesar de serem realmente chamados durante esse estágio. Observe que, diferentemente da versão XSLT, o abstrato da tag da seção é mais facilmente dividida em uma chamada para startSection() e endSection(). Caso contrário, é necessário renderizar o conteúdo do elemento da seção e, em seguida, passar esse conteúdo para a função section() . Nesse caso, o conteúdo é trivial — o resumo da pessoa —mas, em geral, parecia mais direto dividi-lo em duas chamadas de função.

As funções doTechSkills(), doActTypes() e as funções de suporte são bem diretas. Consulte o código de origem anexado para obter detalhes.

Convertendo a Visualização para HTML em PHP

A última peça do lado PHP do quebra-cabeça é resume-view-to-html.php, na Listagem 25.

Listagem 25. resume-view-to-html.php
<?php

function cssLink ($url) { ?>

   <link rel="stylesheet" type="text/css" href="<?= $url ?>"/>

<?
}

function headerLink ($url) { ?>

  <h3><a href="mailto:<?= $url ?>"><?=
$url ?></a></h3>

<?
}

function startSection ($name) { ?>

  <div class="section-title">
    <span class="section-title"><?= $name ?></span>
  </div>
  <div class="section">

<?
}

function endSection() { ?>

  </div>

<?
}

?>

A diferença fundamental entre esse arquivo PHP e sua folha de estilo XSLT análoga é que a função PHP não requer nenhum tipo de função de identidade. Apesar de as camadas de abstração serem praticamente idênticas, o mecanismo de conversão subjacente entre elas é completamente diferente. Na versão PHP, quando o resume-structured-to-view.php estiver concluído, a página é gravada no padrão e mostrada no navegador. A abstração estava no local das funções usadas, enquanto que na versão XSLT a abstração precisava de uma passagem por todo o documento resultante do estágio anterior da transformação.


Resumo

Neste tutorial, você explorou duas abordagens para separar conteúdo da apresentação usando XML:

  • Folhas de estilo XSLT através do módulo XSL em PHP
  • O módulo SimpleXML em PHP

Apesar de os conceitos fundamentais por trás das duas abordagens (XSLT e PHP) serem bem diferentes, é possível estruturar as implementações resultantes em duas linguagens praticamente idênticas, tornando a opção uma questão de preferência e desempenho.


Download

DescriçãoNomeTamanho
Tutorial source codexslt-simplexml-tut-src.tar180KB

Recursos

Aprender

Obter produtos e tecnologias

  • PHP versão 5.2.6: Obtenha PHP 5.2.6 para ser usado neste tutorial.
  • Software de teste IBM para avaliação de produto : Construa seu próximo projeto com software de avaliação disponível para download diretamente a partir do developerWorks, incluindo ferramentas de desenvolvimento de aplicativos e produtos de middleware do DB2®, Lotus®, Rational®, Tivoli®e WebSphere®.

Discutir

Comentários

developerWorks: Conecte-se

Los campos obligatorios están marcados con un asterisco (*).


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


A primeira vez que você entrar no developerWorks, um perfil é criado para você. Informações no seu perfil (seu nome, país / região, e nome da empresa) é apresentado ao público e vai acompanhar qualquer conteúdo que você postar, a menos que você opte por esconder o nome da empresa. Você pode atualizar sua conta IBM a qualquer momento.

Todas as informações enviadas são seguras.

Elija su nombre para mostrar



Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

Los campos obligatorios están marcados con un asterisco (*).

(Escolha um nome de exibição de 3 - 31 caracteres.)

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


Todas as informações enviadas são seguras.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Software livre
ArticleID=387312
ArticleTitle=Separar conteúdo da Apresentação com XSLT, SimpleXML e PHP 5
publish-date=10072008