O que é um proxy na Doutrina 2?

112

Acabei de ler toda a documentação do Doctrine 2, comecei meu próprio sandbox, entendi a maioria dos principes, mas ainda há uma dúvida e não consegui encontrar nenhuma explicação completa no doc.

  1. O que são Proxyaulas?
  2. Quando devo usá-los sobre entidades?

Pelo que eu entendo, as classes de proxy adicionam uma camada para permitir que você adicione alguns outros recursos às suas entidades, mas por que usar um proxy em vez de implementar os próprios métodos na classe de entidade?

Jérémy
fonte

Respostas:

160

ATUALIZAR

Esta resposta contém informações incorretas sobre as diferenças entre objetos proxy e objetos parciais. Veja a resposta de @Kontrollfreak para mais detalhes: https://stackoverflow.com/a/17787070/252591


Objetos proxy são usados ​​sempre que sua consulta não retorna todos os dados necessários para criar uma entidade. Imagine o seguinte cenário:

@Entity
class User {
     @Column protected $id;
     @Column protected $username;
     @Column protected $firstname;
     @Column protected $lastname;

     // bunch of setters/getters here
}

DQL query:

SELECT u.id, u.username FROM Entity\User u WHERE u.id = :id

Como você pode ver, esta consulta não retorna firstname e lastnamepropriedades, portanto, você não pode criar Userobjeto. A criação de uma entidade incompleta pode levar a erros inesperados.

É por isso que o Doctrine criará UserProxyobjetos que suportam o carregamento lento. Quando você tentar acessarfirstname propriedade (que não está carregada), primeiro carregará esse valor do banco de dados.


Quer dizer, por que devo usar um proxy?

Você deve sempre escrever seu código como se não usasse objetos proxy. Eles podem ser tratados como objetos internos usados ​​pelo Doctrine.

Por que o carregamento lento não pode ser implementado na própria entidade?

Tecnicamente, poderia ser, mas dê uma olhada na classe de algum objeto proxy aleatório. Está cheio de códigos sujos, ugh. É bom ter um código limpo em suas entidades.

Você pode me fornecer um caso de uso?

Você está exibindo uma lista dos 25 artigos mais recentes e deseja exibir os detalhes do primeiro. Cada um deles contém uma grande quantidade de texto, portanto, buscar todos esses dados seria um desperdício de memória. É por isso que você não busca dados desnecessários.

SELECT a.title, a.createdAt
FROM Entity\Article a
ORDER BY a.createdAt DESC
LIMIT 25

$isFirst = true;
foreach ($articles as $article) {
    echo $article->getTitle();
    echo $article->getCreatedAt();

    if ($isFirst) {
        echo $article->getContent(); // Article::content is not loaded so it is transparently loaded 
                                     // for this single article.

        $isFirst = false;
    }
}
Crozin
fonte
Obrigado pela sua resposta, no que há de diferente com Partial Object? Quer dizer, por que devo usar um proxy? Por que o carregamento lento não pode ser implementado na própria entidade? Você pode me fornecer um caso de uso?
Jérémy
1
Objetos parciais e objetos proxy são a mesma coisa - eles podem ser tratados como sinônimos. Quanto ao resto das perguntas, verifique minha resposta atualizada.
Crozin
1
Não entendo por que a doutrina não pode criar o objeto se ele tem apenas metade das propriedades. No php, sou capaz de criar um objeto, mesmo sem definir todas as propriedades.
lixadeiras de
1
Esta é uma resposta totalmente incrível e deve estar na documentação.
Jimbo
7
Esta resposta contém alguns equívocos sérios sobre proxies e objetos parciais. Veja minha resposta para entender o porquê.
Kontrollfreak
81

Proxies

Um proxy Doctrine é apenas um wrapper que estende uma classe de entidade para fornecer Lazy Loading para ela.

Por padrão, quando você pede ao Entity Manager por uma entidade que está associada a outra entidade, a entidade associada não será carregada do banco de dados, mas empacotada em um objeto proxy. Quando seu aplicativo então solicita uma propriedade ou chama um método desta entidade com proxy, o Doctrine irá carregar a entidade do banco de dados (exceto quando você solicitar o ID, que é sempre conhecido pelo proxy).

Isso acontece de forma totalmente transparente para o seu aplicativo devido ao fato de que o proxy estende sua classe de entidade.

O Doctrine irá, por padrão, hidratar as associações como proxies de carregamento lento se você não JOINos fizer em sua consulta ou definir o modo de busca para EAGER.


Agora devo acrescentar isso porque não tenho reputação suficiente para comentar em todos os lugares:

Infelizmente, a resposta de Crozin contém informações incorretas.

Se você executar uma consulta DQL como

SELECT u.id, u.username FROM Entity\User u WHERE u.id = :id

você não obterá um objeto de entidade (com proxy), mas uma matriz associativa. Portanto, não é possível carregar lentamente nenhuma propriedade adicional.

Com isso em mente, chega-se à conclusão de que o exemplo de caso de uso também não funcionará. O DQL teria que ser alterado para algo assim para acessar $articlecomo objeto:

SELECT a FROM Entity\Article a ORDER BY a.createdAt DESC LIMIT 25

E a propriedade retornada por getContent()teria que ser uma associação para não carregar as propriedades de conteúdo de todos 25 entidades.


Objetos Parciais

Se você deseja carregar parcialmente as propriedades da entidade que não são associações, você deve informar esta Doutrina explicitamente:

SELECT partial u.{id, username} FROM Entity\User u WHERE u.id = :id

Isso fornece um objeto de entidade parcialmente carregado.

Mas cuidado, pois objetos parciais não são proxies! O Lazy Loading não se aplica a eles. Portanto, usar objetos parciais é geralmente perigoso e deve ser evitado. Leia mais: Objetos Parciais - Doctrine 2 ORM 2 documentação

Kontrollfreak
fonte
1
Obrigado, isto fornece muito mais detalhes sobre como o Doctrine usa Proxies e Partial Objects do que a resposta aceita! E a referência aos documentos também é útil.
Sean the Bean
1
Também para referência, aqui está a seção dos documentos sobre objetos Proxy: doctrine-orm.readthedocs.org/en/latest/reference/…
Sean the Bean
Portanto, ao fazer um carregamento antecipado, é basicamente apenas adicionar conjuntos de resultados?