Como estruturar o sistema de modelos usando PHP simples?


15

Eu estava lendo esta pergunta no stackoverflow:

/programming/104516/calling-php-functions-within-heredoc-strings

e a resposta aceita diz para criar modelos PHP simples como este:

template.php:

<html>
    <head>
        <title><?=$title?> </title>
    </head>
    <body>
        <? = getContent ()?>
    </body>
</html>

index.php:

<? php

$ title = 'Título da demonstração';

função getContent () {
    retornar '<p> Olá mundo! </p>';
}

include ('template.php');

?>

Para mim, o exposto acima não está bem estruturado no sentido de que o template.php depende de variáveis ​​definidas em outros scripts. E você está usando um include () para executar o código quando o faz include('template.php')(ao contrário de include () para incluir uma classe ou uma função que não é executada imediatamente).

Eu sinto que uma abordagem melhor é envolver seu modelo dentro de uma função:

template.php:

<? php

modelo de função ($ title, $ content) {
    ob_start (); ?>

<html>
    <head>
        <title><?=$title?> </title>
    </head>
    <body>
        <? = $ content?>
    </body>
</html>

    <? php return ob_get_clean ();
}

?>

index.php:

<? php

require_once ('template.php');

print template ('Demo Title', '<p> Olá, mundo! </p>');

?>

A segunda abordagem é melhor? Existe uma maneira ainda melhor de fazer isso?

Respostas:


12

Eu não faria a segunda abordagem, pois os parâmetros não são nomeados.

Um artigo bem escrito sobre a descrição de como um sistema de gabaritos deve funcionar vem do Parr; é frequentemente citado por pessoas que escrevem sistemas de gabaritos e / ou estruturas de web-mvc.

Pessoalmente, o que eu normalmente prefiro é implementar uma classe ArrayObject com uma matriz de propriedades, e o modelo se referiria $this->propertyName, o que de fato seria o objeto do modelo $this->property['name']. Isso também pode ser alcançado simplesmente usando __sete __get, portanto:

class Template {
  private $_scriptPath=TEMPLATE_PATH;//comes from config.php
  public $properties;
  public function setScriptPath($scriptPath){
    $this->_scriptPath=$scriptPath;
  }
  public function __construct(){
      $this->properties = array();
  }
  public function render($filename){

   ob_start();
   if(file_exists($this->_scriptPath.$filename)){
     include($this->_scriptPath.$filename);
    } else throw new TemplateNotFoundException();
    return ob_get_clean();
  }
  public function __set($k, $v){
      $this->properties[$k] = $v;
  }
  public function __get($k){
      return $this->properties[$k];
  }
}

e um modelo ficaria assim:

<html>
      <head>
         <title><?=$this->title?></title>
      </head>
      <body>Hey <?=$this->name?></body>
</html>

e invocá-lo ficaria assim:

$view = new Template();
$view->title="Hello World app";
$view->properties['name'] = "Jude";
echo $view->render('hello.inc');

Tanto quanto me lembro, é assim que os antigos mecanismos do Symfony 1.x e do modelo Zend_View se parecem, e para mim tudo bem.


Tinybutstrong faz algo semelhante, bem
— Izkata

como você lida com looping sobre algumas pontuações dizer? você usa um modelo "sub" separado?
— Ryan

Gostaria de saber se existe alguma maneira de se livrar de usar $ this no modelo, por exemplo, apenas $ title ou $ name.
— Ryan

1
@ Ryan Você pode usar extract($this->properties);para extrair as propriedades em variáveis, imediatamente antes da instrução include.
— precisa saber é o seguinte

1

Um modelo sempre dependerá de variáveis ​​externas e, com o PHP sendo uma linguagem dinâmica, não há como impor em tempo de compilação que eles estarão lá. Portanto, a primeira abordagem provavelmente está correta; ainda existem alguns problemas:

  • O exemplo não cuida da codificação HTML da saída, o que significa que você tem possíveis vulnerabilidades XSS.
  • Se uma dessas variáveis ​​não estiver definida, o código emitirá um aviso; você não verá se o aviso faz sentido ou não (talvez a variável seja opcional).

Uma abordagem muito direta é escrever uma função, de preferência com um nome muito curto, que cuide de ambas; chamando isso _()parece ser uma convenção comum. Uma versão simples pode ser assim:

function _($raw) {
    if (isset($raw)) {
        return htmlspecialchars($raw);
    }
}

E então no seu modelo, você faria:

<title><?= _($title) ?></title>

Mais coisas a fazer com a _()função:

  • Dê a ele um segundo argumento para permitir a substituição da htmlspecialcharschamada, nos casos em que você deseja passar HTML para ela em vez de cadeias brutas.
  • Adicione verificação de tipo para que matrizes e objetos não produzam Arraye Object, em vez disso, algo significativo (ou string vazia).
  • Adicione suporte à internacionalização (outro argumento).
  • Quando estiver no modo de depuração, imprima o nome da variável com alguma formatação especial, se não estiver definida. Dessa forma, você pode ver rapidamente se há algo errado com o modelo.

1

O primeiro template.phpé mais fácil de carregar como está no dreamweaver / bluegriffon / o que for e editado por um web designer não experiente do lado do servidor, o segundo ... bem, ainda meio que poderia, mas é muito mais provável que ele se quebre durante o processo de edição .

Eu iria com o primeiro para manter o aspecto de HTML com benefícios , da maneira que os designers de terceiros geralmente preferem.


em que sentido? você quer dizer que não pode ser alterado usando arrastar e soltar? textualmente, o segundo é o mesmo que o primeiro, mas com algumas linhas de PHP em volta. Supondo que sempre tenha essa estrutura, não é mais difícil para eles editarem manualmente.
— Ryan

1
Um bom modelo faz sentido para os desenvolvedores, um ótimo modelo faz sentido para os designers.
— precisa saber é

1

Como o Codeigniter é minha estrutura favorita, proponho uma solução isenta de Codeigniter:

class Template {
    protected $path, $data;

    public function __construct($path, $data = array()) {
        $this->path = $path;
        $this->data = $data;
    }

    public function render() {
        if(file_exists($this->path)){
            //Extracts vars to current view scope
            extract($this->data);

            //Starts output buffering
            ob_start();

            //Includes contents
            include $this->path;
            $buffer = ob_get_contents();
            @ob_end_clean();

            //Returns output buffer
            return $buffer;
        } else {
            //Throws exception
        }
    }       
}

Seu modelo seria semelhante a:

<html>
    <head>
        <title><?=$title?></title>
    </head>
    <body>
        <?=$content?>
    </body>
</html>

E você a renderizaria instanciando a classe, passando dados em uma matriz como um parâmetro construtor e chamando o método 'render':

$data = array('title' => 'My title', 'content' => 'My content');
$tmpl = new Template('template.php', $data);
echo $tmpl->render();

Dessa forma, você não está executando diretamente o código incluído e também mantendo seu modelo legível, para que os designers fiquem felizes.


-3

Php não é uma linguagem de programação. Portanto, escrever com forte digitação não será possível se o seu idioma não tiver um sistema de tipos real. Mas você pode ajudar seu IDE, como PHP-Storm, a fazer o truque que deseja ...

Sua definição de visualização:

interface mySimpleView { 
  public function getTitle(); 
} 

Seu arquivo de modelo:

<?php
/**
* @var $this mySimpleView  <== Your IDE will try to use this type.
*/
?>

<h1><?= $this->getTitle() ?></h1>
<p><?= $this->getContent() ?></p> <!-- <= IDE will warn you !  -->

Eu gosto desta resposta (embora claramente "Php não seja uma linguagem de programação" sempre seja controverso.
— Ronnie
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.