Carregadores de modelos

Os carregadores são o mecanismo que o Latte utiliza para recuperar o código-fonte de seus modelos. Geralmente, os modelos são arquivos armazenados em disco, mas o sistema flexível de carregadores do Latte permite carregá-los de praticamente qualquer lugar ou até mesmo gerá-los dinamicamente.

O que é um Loader?

Normalmente, quando você trabalha com modelos, pensa em arquivos .latte que residem na estrutura de diretórios do seu projeto. Isso é tratado pelo FileLoader padrão do Latte. Entretanto, a conexão entre o nome de um modelo (como 'main.latte' ou 'components/card.latte') e o conteúdo real do código-fonte não precisa ser um mapeamento direto do caminho do arquivo.

É aí que entram os loaders. Um loader é um objeto responsável por pegar um nome de modelo (uma string de identificador) e fornecer ao Latte seu código-fonte. O Latte depende inteiramente do carregador configurado para essa tarefa. Isso se aplica não apenas ao modelo inicial solicitado via $latte->render('main.latte'), mas também a todos os modelos referenciados dentro usando tags como {include ...}, {layout ...}, {embed ...}, ou {import ...}.

Por que usar um carregador personalizado?

  • Carregamento de fontes alternativas:** Buscar modelos armazenados em um banco de dados, um cache (como Redis ou Memcached), um sistema de controle de versão (como Git, com base em um commit específico) ou gerados dinamicamente.
  • Implementação de convenções de nomenclatura personalizadas:** Talvez você queira usar aliases mais curtos para os modelos ou implementar uma lógica de caminho de pesquisa específica (por exemplo, procurar primeiro em um diretório de temas e, em seguida, retornar a um diretório padrão).
  • Adição de segurança ou controle de acesso:** Um carregador personalizado pode verificar as permissões do usuário antes de carregar determinados modelos.
  • Pré-processamento:** Embora geralmente não seja recomendado( é melhor usarpassagens do compilador ), um carregador poderia teoricamente pré-processar o conteúdo do modelo antes de entregá-lo ao Latte.

Você configura o carregador para sua instância Latte\Engine usando o método setLoader():

$latte = new Latte\Engine;

// Usar o FileLoader padrão para arquivos em '/path/to/templates'
$loader = new Latte\Loaders\FileLoader('/path/to/templates');
$latte->setLoader($loader);

Um carregador deve implementar a interface Latte\Loader.

Carregadores incorporados

O Latte oferece vários carregadores padrão:

FileLoader

Esse é o carregador padrão usado pelo Latte\Engine se nenhum outro carregador for especificado. Ele carrega modelos diretamente do sistema de arquivos.

Opcionalmente, você pode definir um diretório raiz para restringir o acesso:

use Latte\Loaders\FileLoader;

// O seguinte só permitirá o carregamento de modelos em /var/www/html/templates
$loader = new FileLoader('/var/www/html/templates');
$latte->setLoader($loader);

// $latte->render('../../../etc/passwd'); // Isso lançaria uma exceção

// Renderizar o modelo localizado em /var/www/html/templates/pages/contact.latte
$latte->render('pages/contact.latte');

Ele resolve nomes de modelos relativos ao modelo atual ao usar tags como {include} ou {layout}, a menos que seja fornecido um caminho absoluto.

Carregador de strings

Esse carregador recupera o conteúdo do modelo de uma matriz associativa em que as chaves são nomes de modelos (identificadores) e os valores são as cadeias de código-fonte do modelo. É particularmente útil para testes ou pequenos aplicativos em que os modelos podem ser armazenados no próprio código PHP.

use Latte\Loaders\StringLoader;

$loader = new StringLoader([
	'main.latte' => 'Hello {$name}, include is below:{include helper.latte}',
	'helper.latte' => '{var $x = 10}Included content: {$x}',
	// Adicione mais modelos conforme necessário
]);

$latte->setLoader($loader);

$latte->render('main.latte', ['name' => 'World']);
// Saídas: Hello World, o include está abaixo:Conteúdo incluído: 10

Se você só precisa renderizar um único modelo diretamente de uma string sem precisar de includes ou herança referenciando outros modelos de string nomeados, pode passar a string diretamente para render() ou renderToString() ao usar StringLoader sem um array:

$loader = new StringLoader;
$latte->setLoader($loader);

$templateString = 'Hello {$name}!';
$output = $latte->renderToString($templateString, ['name' => 'Alice']);
// $output contém "Hello Alice!".

Criação de um carregador personalizado

Para criar seu próprio carregador (por exemplo, para carregar modelos de um banco de dados, cache, controle de versão ou outra fonte), é necessário criar uma classe que implemente a interface Latte\Loader.

Vamos dar uma olhada no que cada método precisa fazer.

getContent (string $name)string

Esse é o método principal do carregador. Sua responsabilidade é recuperar e retornar o conteúdo completo do código-fonte do modelo identificado por $name (conforme passado para $latte->render() ou retornado por getReferredName()).

Se o modelo não puder ser encontrado ou acessado, esse método deve lançar um Latte\RuntimeException.

public function getContent(string $name): string
{
	// Exemplo: Buscar em um armazenamento interno hipotético
	$content = $this->storage->read($name);
	if ($content === null) {
		throw new Latte\RuntimeException("Template '$name' cannot be loaded.");
	}
	return $content;
}

getReferredName (string $name, string $referringName)string

Esse método lida com a resolução de nomes de modelos usados em tags como {include}, {layout}, etc. Quando o Latte encontra, por exemplo, {include 'partial.latte'} dentro de main.latte, ele chama esse método com $name = 'partial.latte' e $referringName = 'main.latte'.

O trabalho do método é resolver $name em um identificador canônico (por exemplo, um caminho absoluto, uma chave de banco de dados exclusiva), que será usado ao chamar outros métodos do carregador, com base no contexto fornecido por $referringName.

public function getReferredName(string $name, string $referringName): string
{
	return ...;
}

getUniqueId (string $name)string

O Latte usa o cache de modelos compilados para melhorar o desempenho. Cada arquivo de modelo compilado precisa de um nome exclusivo derivado do identificador do modelo de origem. Esse método fornece uma string que identifica exclusivamente o modelo $name.

Para modelos baseados em arquivos, o caminho absoluto pode funcionar. Para modelos de banco de dados, é comum a combinação de um prefixo e o ID do banco de dados.

public function getUniqueId(string $name): string
{
	return ...;
}

Exemplo: Carregador de banco de dados simples

Este exemplo demonstra a estrutura básica de um carregador que recupera modelos armazenados em uma tabela de banco de dados chamada templates com as colunas name (identificador exclusivo), content e updated_at.

use Latte;

class DatabaseLoader implements Latte\Loader
{
	public function __construct(
		private \PDO $db,
	) {
	}

	public function getContent(string $name): string
	{
		$stmt = $this->db->prepare('SELECT content FROM templates WHERE name = ?');
		$stmt->execute([$name]);
		$content = $stmt->fetchColumn();
		if ($content === false) {
			throw new Latte\RuntimeException("Template '$name' not found in database.");
		}
		return $content;
	}

	// Este exemplo simples pressupõe que os nomes dos modelos ("homepage", "article" etc.)
	// são IDs exclusivos e os modelos não fazem referência uns aos outros de forma relativa.
	public function getReferredName(string $name, string $referringName): string
	{
		return $name;
	}

	public function getUniqueId(string $name): string
	{
		// Usar um prefixo e o próprio nome é único e suficiente aqui
		return 'db_' . $name;
	}
}

// Uso:
$pdo = new \PDO(/* connection details */);
$loader = new DatabaseLoader($pdo);
$latte->setLoader($loader);
$latte->render('homepage'); // Carrega o modelo com o nome "homepage" do banco de dados

Os carregadores personalizados oferecem controle total sobre a origem dos modelos do Latte, permitindo a integração com vários sistemas de armazenamento e fluxos de trabalho.

versão: 3.0