Esta pattern descreve como recuperar dados do banco somente se for necessário e na hora de uso sem afetar o resto do sistema. O principal conceito é dar a sensação para os outros objetos de que o dado está disponível e pronto para usar ocultando o carregamento tardio sem que isso afete quem usa.
Exemplo básico:
Essa forma é bem comum e usada em várias linguagens.
<?php
// classe de categoria qualquer
class Category
{
protected $id = null;
public function __construct($id = null)
{
$this->id = (int) $id;
}
}
// uma classe que irá fazer lazy da categoria para outros objetos
class Post
{
protected $id = null;
protected $category_id = null;
protected $title = 'Titulo';
/**
* Buffer, aqui será guardada a categoria após ser carregada
* @var Category
*/
protected $category = null;
public function __construct($id = null)
{
if ($id) {
$this->id = (int) $id;
$this->category_id = 4; // carrega os dados do banco
}
}
public function setTitle($title)
{
$this->title = $title;
}
public function getTitle()
{
return $this->title;
}
/**
* Retorna o objeto da categoria do post
*
* @return Category
*/
public function getCategory()
{
// lazy, verifica se já foi carregada, ou carrega...
if ($this->category === null) {
$this->category = new Category($this->category_id);
}
return $this->category;
}
}
?>
Não tem muito o que falar sobre o método acima.
Modo Avançado:
O PHP possui um recurso interessante para fazer um lazy bem mais elaborado. O método mágico __get dos objetos permite tratar o acesso a atributos que não existem e retornar um valor. Em nosso caso, vamos utiliza-lo para fazer lazy carregando o objeto em um atributo.
<?php
class Comment
{
protected $id;
protected $post_id = null;
/**
*
* @var Post
*/
public $post;
public function __construct($id = null)
{
if ($id) {
$this->id = (int) $id;
$this->post_id = 5;
}
// remove o membro para funcionar no __get
}
/**
* Método mágico do PHP que é utilizado para acessar "atributos inexistentes"
* Esse foi o motivo do unset($this->post), após a criação ele será acessado
* diretamente da variavel.
*
* @return mixed
*/
protected function __get($name)
{
switch ($name) {
case 'post':
// lazy, carrega o objeto no membro e retorna
if ($this->post_id) {
// debug :)
$this->post = new Post($this->post_id);
return $this->post;
} else {
return null;
}
break;
default:
break;
}
}
}
// Exemplo de uso
$comment = new Comment(3);
echo $comment->
post->
getTitle() .
"\n";
echo $comment->
post->
getTitle() .
"\n";
?>
Output:
Lazy
Titulo
Titulo
Existe também o método __set($name, $value) e da mesma forma só funciona se o atributo não existir. Note que você não precisa usar a váriavel de acesso com o mesmo nome do atributo.
<?php
class Test
{
protected $var = null;
protected function __get($name)
{
if ($name == 'test') {
if ($this->var == null) {
$this->var = 'Aham!';
}
return $this->var;
}
}
}
$test = new Test();
?>
Output:
Aham!
Dessa forma o membro nunca irá existir e sempre passará pela verificação do __get. É útil para tornar atributos readonly combinando com o __set ou atualizando os dados caso o objeto for substituido (ex: $this->post_id = $value->getKey()).