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.