WordPress - controlando a exibição dos dados

No post anterior, criamos a interface para nossa view. Hoje veremos como criar a base que vai controlar o conteúdo retornado! Vem comigo? ;)

O controller

Lembra que criamos o arquivo functions/controllers/controller-single.php? A primeira coisa que precisamos fazer então, é incluir esse arquivo na nossa estrutura. Mas onde?

No functions.php! Esse arquivo é a “cola” que vai juntar todo o controle da nossa aplicação às nossas views.

Vamos então incluir nosso controller no início do nosso functions.php, logo abaixo do require do security.php:

1
2
3
<?php
require_once __DIR__ . '/functions/security.php';
require_once __DIR__ . '/functions/controllers/controller-single.php';

Só um pequeno adendo: o Pedro Diaz me questionou nesse post que, quando ele tentava incluir o security.php usando um caminho relativo, o arquivo não era encontrado. Para resolver o problema, podemos fazer como eu fiz no exemplo acima: utilizar a constante mágica do PHP, __DIR__, que vai retornar o diretório atual. A partir daí, você aponta para onde está o arquivo.

Essa constante só funciona à partir do PHP 5.3. Então, se você estiver usando uma versão anterior a essa, utilize dirname( __FILE__ ) no lugar :)

Agora voltando ao post: no arquivo controller-single.php, vamos criar a classe que vai gerenciar nosso conteúdo:

1
2
3
4
5
6
7
8
9
10
<?php
require_once __DIR__ . '/../security.php';

class Controller_Single {
public function __construct() {
$this->thumbnail = 'thumbnail';
$this->title = 'title';
$this->content = 'content';
}
}

Já expliquei sobre a boa prática de não fechar o <?php quando você tem só código PHP no arquivo, né? ;)

Certo, agora precisamos instanciar essa classe na nossa view (single.php). Coloque o código no início do arquivo, logo abaixo de get_header():

1
$single_post = new Controller_Single();

Essa estrutura é a base principal que precisaremos para que o conteúdo seja mostrado corretamente na nossa view.

Se você não conhece Orientação à objetos no PHP, vou explicar rapidamente pra você entender o que foi feito, mas recomendo que você leia sobre o assunto, pois vai ser bastante importante para nós, daqui pra frente ;)

Pra quem não conhece, o que pode parecer mais estranho é o método __construct(). Quando você cria uma classe PHP, usando class Nome_Da_Classe, você pode utilizar o método __construct() para “construir” o objeto, quando este instanciar a classe. Um outro detalhe que você deve ter reparado é o $this. Esse cara representa o objeto em si. Ou seja: toda propriedade ou método de dentro da class, pode ser chamado ali dentro via $this->property_name para chamar propriedades, ou $this->method_name(), para chamar métodos.

No arquivo single.php, repare que utilizamos o new Controller_Single() para instanciar a classe. Agora, o $this, que você vê dentro da classe, está representado pelo $single_post. Por isso nós conseguimos chamar o thumbnail, title e content, que chamamos de propriedades da classe, com a sintaxe de objetos:

1
2
3
echo $this->thumbnail;
echo $this->title;
echo $this->content;

Basicamente, só para que você não fique perdido nos nomes:

  • Quando você cria uma variável em qualquer parte do seu código PHP, o nome que se dá a ela é variável. Quando você cria uma variável dentro de uma classe PHP, o nome dado é propriedade da classe.
  • Quando você cria uma função em qualquer parte do seu código, damos o nome de função. Mas quando você cria uma função dentro de uma classe, ela é chamada método.

Toda propriedade de uma classe, fica acessível para todos os métodos da classe, ou seja, se criarmos um outro método dentro da nossa classe, nós vamos conseguir acessar o valor de $this->thumbnail, por exemplo, dentro desse novo método, sem precisar usar o global. Isso deixa o código muito mais limpo e organizado.

Bom, isso foi só uma introdução pra contextualizar, pra quem ainda não conhece orientação a objetos. Se ainda ficar alguma dúvida, você pode perguntar nos comentários, ok? Vamos em frente? ;)

Sobre o conteúdo

Ainda temos um conteúdo fictício, que colocamos só para ver as coisas funcionando. Precisamos usar o conteúdo do post mesmo. Mas antes disso, vamos alterar um pouco nosso código.

Nós conseguimos instanciar a classe Controller_Single() dentro de single.php, porque, dentro da estrutura do WordPress, ele carrega o arquivo functions.php antes de qualquer template dentro do seu tema. Então, tudo o que você adiciona no functions.php, automaticamente fica disponível em todos os seus templates.

Mas nesse caso, nós não iremos instanciar essa classe diretamente na nossa view. Vamos fazer uso dos hooks, para que possamos chamar os dados da single somente quando realmente eles forem necessários. Com isso, vamos aprender também como criar nossos próprios hooks personalizados!

Hooks

Como já falei em um artigo anterior, hooks são ganchos deixados por todo o código do WordPress pelos seus desenvolvedores, para que você possa “pendurar” trechos de código em momentos específicos.

Por exemplo: nós temos o hook the_title, que é executado sempre que você chama a template tag the_title(), ou retorna o título com get_the_title(). Todas as template tags tem os dois formatos: o que “dá echo“ no conteúdo, e o que retorna o conteúdo.

Pra quê isso?

A vantagem disso é que, com as template tags (que nada mais são do que simples funções do PHP), que dão echo no conteúdo, você não precisa escrever o echo. Já se você quiser salvar o valor em uma variável, você utiliza a versão prefixada com get_, que vai retornar o valor, ao invés de printar. Basicamente a regra para essas funções é colocar o get_ na frente, sempre que você quiser retornar, salvo em alguns casos específicos, como quando você quer pegar um link: a função que dá echo é a the_permalink(). E a função que retorna é a get_permalink(). O recomendado é SEMPRE - Quando eu disse mesmo? SEMPRE! - consultar o Codex quando você tiver dúvidas :)

Explicadas as template tags, vamos aos hooks!

Temos dois formatos de hooks que podemos utilizar: actions e filters.

A sintaxe para usá-los é a seguinte:

1
2
add_action( 'hook_name', 'function_callback' );
add_filter( 'hook_name', 'function_callback' );

Pra que servem as duas funções?

Actions

De uma forma bem resumida: as actions “dão echo“ em valores, ou não precisam retornar nada. Você passa como primeiro parâmetro da função add_action o nome do hook onde será executada a função de callback, passada no segundo parâmetro.

Lembra que utlizamos o hook after_setup_theme no nosso functions.php, para adicionar suporte às thumbnails? Vamos lembrar como nós fizemos? Olha só o nosso functions.php atualizado:

1
2
3
4
5
6
7
8
&lt;?php
require_once __DIR__ . '/functions/security.php';
require_once __DIR__ . '/functions/controllers/controller-single.php';

add_action( 'after_setup_theme', 'setup_features' );
function setup_features() {
add_theme_support( 'post-thumbnails' );
}

Isso significa que, em algum lugar do código do WordPress, existe o gancho after_setup_theme, que deve executar após o setup do tema, como o próprio nome diz :P

Com o add_action, nós “penduramos” nesse gancho a função setup_features. Quando esse gancho for executado, nossa função também será :D

Para criar um gancho para uma action, nós usamos a função do_action().

Por exemplo: se você colocar em qualquer lugar do seu código um do_action( 'nada' ), você acabou de deixar um gancho ali. Você pode pendurar qualquer coisa nesse gancho, a partir de qualquer outra parte do seu código, usando:

1
add_action( 'nada', 'funcao_callback' );

Deu pra entender até aqui?

Filters

E os filters, são ganchos que retornam valores.

Quando você usa:

1
add_filter( 'hook_name', 'function_callback' );

A sua função de callback deve sempre ter um return no final, retornando um valor qualquer, mas NUNCA um echo.

Por que?

Porque os ganchos para filters são atribuídos à variáveis, ou então, onde é deixado o gancho, ele já tem um echo antes. A função que deixa o gancho para filters é a apply_filters.

Então, normalmente você vai ter algo assim:

1
$wp_content = apply_filters( 'the_content', 'content_default' );

Então, quando você usar:

1
2
3
4
add_filter( 'the_content', 'callback_function' );
function callback_function( $content_default ) {
return 'content';
}

O valor retornado pela função será passado para a variável $wp_content. Perceba o segundo parâmetro nessa função: ele é o valor padrão que a variável vai receber. Ou seja, se não tiver nada “pendurado” nesse gancho, $wp_content irá receber o valor 'content_default'. Perceba na nossa função de callback que, os parâmetros adicionais que passamos em apply_filters() (sim, pode ter mais de um), são transferidos como parâmetros da função de callback :D

Agora vamos ver como isso se aplica na prática! Como usar no nosso tema?

Usando o hook no nosso template

No nosso single.php, nós iremos trocar a linha:

1
$single_post = new Controller_Single();

Pelo seguinte código:

1
2
the_post();
$single_post = apply_filters( 'get_single_content', false );

Primeiro chamamos the_post(), pois o WP só irá usar esse template se um post existir. Então já chamamos a função the_post() para que possamos utilizar as template_tags() apontando as informações para o post em questão. Devemos sempre deixar a chamada do post() no arquivo de template principal, pois isso garante que, se tivermos partials, as template_tags() funcionarão também nessas partials.

Pronto! Deixamos um gancho na nossa view single.php, que irá receber todo o conteúdo que precisamos para mostrar os dados do post. Isso nos dá uma liberdade muito maior de usar a classe Controller_Single somente para organizar o conteúdo enviado para a view single.php, e trabalhar com outros objetos dentro dessa classe :D

E adivinha onde iremos pendurar código para esse gancho get_single_content?

Isso mesmo! No nosso controller!

Vamos então mudar um pouco a estrutura do Controller_Single.php. Deixe assim nosso métod __construct():

1
2
3
4
5
6
7
8
&lt;?php
require_once __DIR__ . '/../security.php';

class Controller_Single {
public function __construct() {
add_filter( 'get_single_content', array( $this, 'get_single_content' ) );
}
}

E, no final do arquivo, instancie a classe:

1
new Controller_Single();

Quando o arquivo for executado, a classe será instanciada automaticamente, e o gancho get_single_content, que está em __construct(), será ativado. Então, somente quando for encontrado o apply_filters( 'get_single_content' ), é que a função passada no segundo parâmetro será executada.

Perae, mas você falou que o segundo parâmetro deveria ser o nome de uma função! Por que você passou um array?

Porque nós queremos usar um método da classe Controller_Single. Quando trabalhamos com orientação a objetos no PHP, sempre que precisamos passar uma função como String, que será executada em um momento futuro, nós usamos um array, passando no primeiro parâmetro o nome da classe em formato de String ou, se for um método da classe que você está, pode usar o $this para referenciar essa classe.

Fazer isso é o mesmo que dizer:

_Ao encontrar o gancho get_single_content, execute o método $this->get_single_content()_.

Agora, vamos pegar o conteúdo do post mesmo. Ainda na classe Controller_Single, crie um método público, chamado get_single_content (sim, o mesmo nome do gancho para facilitar encontar depois).

Nesse método, você vai então fazer o loop, ou retornar as informações que você montou na sua interface, no arquivo single.php. Então, faça isso no controller Controller_Single:

1
2
3
4
5
6
7
public function get_single_content() {
$single_content = new StdClass();
$single_content->thumbnail = has_post_thumbnail() ? get_the_post_thumbnail() : '';
$single_content->title = get_the_title();
$single_content->content = get_the_content();
return $single_content;
}

A classe StdClass() que você pode ver sendo instanciada ali, é uma classe parão do PHP, em branco, para que você possa criar objetos rapidamente, sem precisar criar uma nova classe.

Perceba que a lógica do post_thumbnail nós fizemos ali no controller. Nada de lógica na view! Se precisamos testar qualquer coisa, que seja no controller, ou, se o projeto começar a ficar muito grande, separar os controllers em services.

Executando agora nosso site, podemos ver que tudo funciona como deveria!

Essa é a forma que eu uso hoje para separar responsabilidades! Gostou? Achou fácil? Difícil? Quero saber sua opinião, e como você estrutura seus temas no WP! Comente! :D

Link para o índice dessa série: https://blog.da2k.com.br/2015/01/11/indice-da-serie-como-criar-temas-para-wordpress/

Post anterior: https://blog.da2k.com.br/2015/02/10/wordpress-separando-as-responsabilidades/

Próximo post: https://blog.da2k.com.br/2015/02/22/wordpress-criando-um-formulario-de-contato-sem-plugin-parte-1/

Sobre o #1postperday: https://blog.da2k.com.br/2014/12/31/um-post-por-dia/

Tem alguma sugestão para os próximos posts do #1postperday? Deixe ela aqui: https://github.com/fdaciuk/fdaciuk.github.io/issues/1