Ir para conteúdo

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

felipe_zmm

Iniciante em OO

Recommended Posts

Bem, se sua intenção é proteger contra session hijacking, resetando o id da sessão, pode usar session_regenerate_id()

 

Você tem um altíssimo nível de acoplamento, este código não é testável pois comete erros. Encapsule as funções de sessions em uma classe e injete essa classe, a mesma coisa com o Db, não instancie no método logo, isso causa acoplamento e intestabilidade. Além do que, do ponto de vista da Orientação a Objetos, isso é uma violação ao SRP, você não deveria ter a responsabilidade de conectar a sessão, conectar ao banco, etc. Injete sempre as dependências.

 

Testabilidade = possível cobrir por testes unitários com facilidade

Compartilhar este post


Link para o post
Compartilhar em outros sites

Bom, fiz algumas alterações nas classes. Não sei se melhorou alguma coisa, mas enfim... Seguem:

 

class BD extends PDO
    {   
        private $dsn = 'mysql:host=localhost;dbname=loja';
        private $usuario = 'root';
        private $senha = '';
        private $con;
        
        public function __construct()
        {
            try
            {
                $this->con = parent::__construct($this->dsn, $this->usuario, $this->senha);
                
                return $this->con;
            }
            catch (PDOException $e)
            {
                echo "Erro! {$e->getMessage()}";
            }
        }
        
        public function __destruct()
        {
            $this->con = null;
        }
    }

 

require_once 'BD.class.php';
    
    class ControleLogin
    {
        //atributos
        private $login;
        private $senha;
        
        //variáveis da classe
        private $bd;
        private $tabela = 'usuarios';
        
        public function __construct()
        {
            $this->bd = new BD();
        }
        
        public function validarUsuario($login, $senha)
        {   
            $this->login = $login;
            $this->senha = $this->gerarHash($senha);
            
            $qry = "SELECT COUNT(id) FROM {$this->tabela} "
                 . "WHERE login = :login AND senha = :senha AND status = '1'";
                 
            $stmt = $this->bd->prepare($qry);
            $stmt->bindParam(':login', $this->login);
            $stmt->bindParam(':senha', $this->senha);
            $stmt->execute();
                
            if ($stmt->fetchColumn() == 1)
            {
                $this->gerarSessao();
                    
                return true;
            }
                
            return false;
        }
        
        public function logout()
        {
            session_start();
            
            $_SESSION = array();
            session_destroy();  
        }
        
        public function usuarioLogado()
        {
            session_start();
            
            return (isset($_SESSION['login'])) && (isset($_SESSION['validado']));
        }
        
        private function gerarHash($senha)
        {
            return sha1($senha);
        }
        
        private function gerarSessao()
        {
            session_start();
            
            $_SESSION['login'] =  $this->login;
            $_SESSION['validado'] = 1;
        }
    }
require_once 'BD.class.php';
    require_once 'Usuario.class.php';
    
    class UsuarioMapper
    {
        private $bd;
        private $tabela = 'usuarios';
        
        public function __construct()
        {
            $this->bd = new BD();
        }
        
        public function incluir(Usuario $usuario)
        {
            $qry = "INSERT INTO {$this->tabela}(id, usuario, senha, email, nome, sobrenome, status, data_cadastro) "
                 . "VALUES(null, :login, :senha, :email, :nome, :sobrenome, '1', CURDATE())";
            
            try
            {
                $stmt = $this->bd->prepare($qry);
                $stmt->bindParam(':login', $usuario->getLogin());
                $stmt->bindParam(':senha', $usuario->getSenha());
                $stmt->bindParam(':email', $usuario->getSenha());
                $stmt->bindParam(':nome', $usuario->getNome());
                $stmt->bindParam(':sobrenome', $usuario->getSobrenome());
                $stmt->execute();
                
                $ultimoId = $this->bd->lastInsertId();
                $usuario->setId($ultimoId);
                
                return true;
            }
            catch (PDOException $e)
            {
                echo $e->getMessage();
                
                return false;
            }     
        }
        
        public function listar()
        {
            $stmt = $this->bd->query("SELECT id, nome, sobrenome, email, login FROM {$this->tabela} ORDER BY nome");
            
            while($res = $stmt->fetch(PDO::FETCH_OBJ))
            {
                $usuario = new Usuario();
                $usuario->setId($res->id);
                $usuario->setNome($res->nome);
                $usuario->setSobrenome($res->sobrenome);
                $usuario->setEmail($res->email);
                $usuario->setLogin($res->login);
                
                $listaUsuarios[] = $usuario;
            }
            
            return $listaUsuarios;
        }
    }

 

Não coloquei a classe Usuario porque a mesma contém apenas suas propriedades, getters e setters.

 

Enrico, desculpe a ignorância, mas quanto a injetar as classes de BD e Sessão você se refere a que exatamente ? Se for manter em classes separadas, já não estou fazendo isso com a classe BD ? Removi o instanciamento da classe BD dos métodos e passei pro construtor. Se este modo for incorreto, qual a melhor maneira de se fazer isso ?

 

Para constar, agradeço a todos pela paciência e boa vontade nas respostas.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Algo que ainda não ficou claro pra mim é se ter a minha classe Usuario apenas com suas propriedades, getters e setters está errado.

 

Segundo o Evandro disse: "De modo geral, sem um excelente motivo, uma classe que apenas armazena estados é incorreta.".

 

Mas só fiz dessa forma porque o pattern prega esse modelo, pelo menos é o que diz e exemplifica o livro do Pablo Dall'Oglio.

Compartilhar este post


Link para o post
Compartilhar em outros sites

É muito sábio também iniciar com frameworks para ter uma base maior e mais didática sobre como a orientação a objeto funciona e se relaciona, E para você prosseguir dali, é fácil fácil! Comecei com CodeIgniter, é organizado, tem uma certa comunidade, documentação excelente, com n exemplos. Sem dúvida, uma mão na roda para quem inicia com OO!

Compartilhar este post


Link para o post
Compartilhar em outros sites

É muito sábio também iniciar com frameworks para ter uma base maior e mais didática sobre como a orientação a objeto funciona e se relaciona, E para você prosseguir dali, é fácil fácil! Comecei com CodeIgniter, é organizado, tem uma certa comunidade, documentação excelente, com n exemplos. Sem dúvida, uma mão na roda para quem inicia com OO!

 

Eu estudei até que bastante do CodeIgniter, mas eu particularmente prefiro entender como a coisa funciona fazendo na unha antes de partir pra um framework. Tenho receio de que eu acabe virando programador de framework x ou y sem saber direito como implementar orientação a objetos sozinho.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Eu estudei até que bastante do CodeIgniter, mas eu particularmente prefiro entender como a coisa funciona fazendo na unha antes de partir pra um framework. Tenho receio de que eu acabe virando programador de framework x ou y sem saber direito como implementar orientação a objetos sozinho.

 

Faça ambos, então!

Compartilhar este post


Link para o post
Compartilhar em outros sites

Mas não é isso que meu UsuarioMapper faz ? Ele recebe uma instância da classe Usuario e persiste as informações no banco de dados.

Sim. Sua arguição não era a respeito de estar separando demais as camadas? Você separa as tarefas relacionadas ao usuário em User, UserMapper e ControleLogin exatamente para ter os escopos bem definidos. Veja que minha declaração permanece válida: "Um User não salva nada nem ninguém". Quem está salvando é o UserMapper que tem exatamente esta função: mapear algo para outro algo

Evandro,

 

No seu primeiro post neste tópico você ficou "assustado" com o fato de eu ter dado um session_start para depois dar um session_destroy. Mas pelo que eu sempre soube para qualquer manipulação de sessão que eu fizer preciso iniciá-la, não ? Mesmo assim fui tentar remover o session_start do método logout e o mesmo parou de funcionar como deveria, não limpando a sessão.

PHP_SESSION_ACTIVE === session_status() and session_destroy();
Novamente a questão das responsabilidades. Não é responsabilidade de um destruidor iniciar uma sessão. Verificamos se já existe uma sessão e, caso positivo, destruímos.

 

Algo que ainda não ficou claro pra mim é se ter a minha classe Usuario apenas com suas propriedades, getters e setters está errado.

 

Segundo o Evandro disse: "De modo geral, sem um excelente motivo, uma classe que apenas armazena estados é incorreta.".

Classes sem métodos são ruins. Classes sem propriedades são ruins. Estáticos são ruins. Singletons são ruins. [inline]final[/inline] é ruim. Prefira composição à herança. Programe para uma Interface, não para uma implementação. Princípio de responsabilidade única. Princípio aberto-fechado. Princípio de substituição de Liskov. Princípio de segregação de Interfaces. Princípio de inversão de dependências.

 

São orientações, conselhos. Não normas. Não há verdade absoluta. Não há um conceito que abranja todos os cenários possíveis da programação. Veja que há Design Patterns datam da década de 70! A mágica está na flexibilidade. Estes conceitos existem como guias para que você conheça os cenários específicos onde quebrar as regras.

 

Mais importante que fazer do jeito certo é saber o quê está fazendo e, principalmente, porque.

 

Creio que seja este seu objetivo desde que iniciou o tópico.

 

Se desejar, podemos nos aprofundar no DataMapper e entender porque quebramos as regras.

Compartilhar este post


Link para o post
Compartilhar em outros sites

CodeIgniter organizado? desculpe, mas com todo respeito, reveja seus conceitos sobre organização. Ele é um framework porco, como a maioria. Eu acho que se um framework se vende como produtivo ou resolução de tudo, está na cara que ele usa convenção para cacete, método estático à solta e o pior: vai ser inflexível. Flexibilidade em um framework não é simplesmente escolher sua nomenclatura ou coisas do tipo como sr. Igniter diz, flexibilidade é você escolher cada parte do sistema, um framework precisa ser modular, hoje em dia os melhores são os componentes, como os do Aura e do Respect, mas também o Symfony2 e ZF2.

 

Voltando ao tópico:

 

:seta: não use herança (até que você realmente tenha a necessidade, poucos casos).

 

:seta: injete as depencências sempre. não crie um acoplamento. veja dependency injection.

 

:seta: use autoload ao invés de require para todo lado.

 

:seta: pare de usar ty e catch por todo lado, você pode simplesmente usar set_exception_handler e colocar uma função para manipular exceptions.

 

:seta: não use return false; a não ser que seja um método de retorno booleano (true ou false), que são métodos que começam geralmente com is ou has (isValid, hasToken, etc.).

 

:seta: não use echo direto no código, uma questão de bom senso, vc vai morrer no erro "headers already sent".

 

:seta: para desconectar uma conexão de db pelo PDO, use $variavelQuePossuiAInstanciaDoPDO->disconnect(); pois colocar o valor do PDO como nulo não vai adiantar e além de tudo não faz sentido remover objetos, já que o PHP possui garbage collection.

 

:seta: pense que uma classe só pode fazer uma coisa, não significa ter um ou dois métodos, mas não valide e conecte no banco direto, tenha classes só para isso, controladas por um mediador.

 

:seta: não use funções sensíveis (session_*, header, setcookie) diretamente, crie classes que implementam esses métodos (classes muito chamadas de Wrappers) e as injete na classe que precisa.

 

:seta: jamais, nunquinha coloque dados que sejam configurações em uma propriedade por padrão, como possui na classe do Banco, lembre-se que as classes devem ser reutilizáveis.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Classes sem métodos são ruins. Classes sem propriedades são ruins. Estáticos são ruins. Singletons são ruins. [inline]final[/inline] é ruim. Prefira composição à herança. Programe para uma Interface, não para uma implementação. Princípio de responsabilidade única. Princípio aberto-fechado. Princípio de substituição de Liskov. Princípio de segregação de Interfaces. Princípio de inversão de dependências.

 

É que minha classe Usuario não contém apenas propriedades ou apenas métodos. Contém as propriedades do Usuario (iguais aos da tabela usuarios do banco de dados) e seus getters e setters. A não ser que vc não esteja considerando os getters e setters como métodos.

 

 

Se desejar, podemos nos aprofundar no DataMapper e entender porque quebramos as regras.

 

 

Se puder me explicar eu agradeço.

 

 

 

:seta: para desconectar uma conexão de db pelo PDO, use $variavelQuePossuiAInstanciaDoPDO->disconnect(); pois colocar o valor do PDO como nulo não vai adiantar e além de tudo não faz sentido remover objetos, já que o PHP possui garbage collection.

 

:seta: pense que uma classe só pode fazer uma coisa, não significa ter um ou dois métodos, mas não valide e conecte no banco direto, tenha classes só para isso, controladas por um mediador.

 

Mas salvo engano o PDO não tem o método disconnect. Aprendi a colocar como null nos exemplos do manual do PHP.

 

<?php
$dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass);
// use the connection here


// and now we're done; close it
$dbh = null;
?>

 

Quanto à conectar no banco na classe ControleLogin não sei o que fazer exatamente. Antes eu chamava a conexão no método validarUsuario, mas me disseram pra não fazer isso. Passei pro construtor, mas essa parece também não ser a melhor opção, já que a classe possui um único método que utiliza o banco de dados. Então o que fazer ? Conectar no banco fora da classe antes de utilizar o método validarUsuario ? O que pensei em fazer é no construtor da classe BD passar as configurações de conexão, resolvendo assim o problema de fixar as configurações e adicionar um método conectar e outro desconectar. Resolveria ?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Felipe, leia sobre dependency injection. E getters e setters são métodos sim. Não que você não possa ter vários em getters e setters, mas não tente inchar a classe com eles e outros métodos, tente deixar suas classes limpas, coerentes e coesas.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Felipe, leia sobre dependency injection. E getters e setters são métodos sim. Não que você não possa ter vários em getters e setters, mas não tente inchar a classe com eles e outros métodos, tente deixar suas classes limpas, coerentes e coesas.

 

Fiz isso e alterei algumas coisas nas classes, se puderem me dizer se melhorou:

 

    class ControleLogin
    {
        private $login;
        private $senha;
        
        public function validarUsuario($login, $senha, PDO $bd, $tabela)
        {   
            $this->login = $login;
            $this->senha = $this->gerarHash($senha);
            
            $qry = "SELECT COUNT(id) FROM {$tabela} "
                 . "WHERE login = :login AND senha = :senha AND status = '1'";
                 
            $stmt = $bd->prepare($qry);
            $stmt->bindParam(':login', $this->login);
            $stmt->bindParam(':senha', $this->senha);
            $stmt->execute();
                
            if ($stmt->fetchColumn() == 1)
            {
                $this->gerarSessao();
                    
                return true;
            }
                
            return false;
        }
        
        public function logout()
        {
            session_start();
            
            $_SESSION = array();
            session_destroy();  
        }
        
        public function usuarioLogado()
        {
            session_start();
            
            return (isset($_SESSION['login'])) && (isset($_SESSION['validado']));
        }
        
        private function gerarHash($senha)
        {
            return sha1($senha);
        }
        
        private function gerarSessao()
        {
            session_start();
            
            $_SESSION['login'] =  $this->login;
            $_SESSION['validado'] = 1;
        }
    }
    class UsuarioMapper
    {
        private $bd;
        private $tabela;
        
        public function __construct(PDO $bd, $tabela)
        {
            $this->bd = $bd;
            $this->tabela = $tabela;
        }
        
        public function incluir(Usuario $usuario)
        {
            $qry = "INSERT INTO {$this->tabela}(id, usuario, senha, email, nome, sobrenome, status, data_cadastro) "
                 . "VALUES(null, :login, :senha, :email, :nome, :sobrenome, '1', CURDATE())";
            
            try
            {
                $stmt = $this->bd->prepare($qry);
                $stmt->bindParam(':login', $usuario->getLogin());
                $stmt->bindParam(':senha', $usuario->getSenha());
                $stmt->bindParam(':email', $usuario->getSenha());
                $stmt->bindParam(':nome', $usuario->getNome());
                $stmt->bindParam(':sobrenome', $usuario->getSobrenome());
                $stmt->execute();
                
                $ultimoId = $this->bd->lastInsertId();
                $usuario->setId($ultimoId);
                
                return true;
            }
            catch (PDOException $e)
            {
                echo $e->getMessage();
                
                return false;
            }     
        }
        
        public function listar()
        {
            $stmt = $this->bd->query("SELECT id, nome, sobrenome, email, login FROM {$this->tabela} ORDER BY nome");
            
            while($res = $stmt->fetch(PDO::FETCH_OBJ))
            {
                $usuario = new Usuario();
                $usuario->setId($res->id);
                $usuario->setNome($res->nome);
                $usuario->setSobrenome($res->sobrenome);
                $usuario->setEmail($res->email);
                $usuario->setLogin($res->login);
                
                $listaUsuarios[] = $usuario;
            }
            
            return $listaUsuarios;
        }
    }

 

Ainda não apliquei todas as dicas que me passaram, mas vou trabalhar nisso na medida do possível. Lendo sobre o dependency injection, me ocorreram alguns pensamentos.

 

1 - Minha classe BD se tornou inútil (acho que ela já era antes), então parei de usá-la e simplesmente instancio um objeto PDO e passo-o como parâmetro:

 

    function __autoload($classe)
    {
        require_once("classes/{$classe}.class.php");    
    }
    
    $usuario = $_POST['usuario'];
    $senha = $_POST['senha'];
    
    $ctrlLogin = new ControleLogin();
    $bd = new PDO('mysql:host=localhost;dbname=loja', 'root', '');
    
    if ($ctrlLogin->validarUsuario($usuario, $senha, $bd, 'usuarios'))
    {
        header('Location: /loja/home_admin.php');
    }
    else
    {
        header('Location: /loja/login.php');
    }

2 - Devo utilizar o autoload nos arquivos de classes ou da forma como fiz, no próprio script que instancia as classes ? Algo que me deixa confuso é que com o paradigma OO eu tenho a impressão de que devo deixar de lado as práticas anteriores, e me veio a dúvida se posso incluir essa função autoload em um arquivo e chamá-lo por include.

 

3 - Quanto a criar uma classe para iniciar e destruir sessão, esta não seria um pouco "inútil" ? Eu teria um método iniciarSessao que apenas chama session_start (é claro que posso fazer outras verificações) e um outro método destruirSessão que a limpa e destrói. Me parece uma classe sem propósito real de certa forma. Talvez eu tenha entendido de forma errada a sugestão.

Compartilhar este post


Link para o post
Compartilhar em outros sites

1 - Sim, ela era inútil e você resolveu um problema: acoplamento.

 

2 - Você sempre terá um arquivo de index.php, onde parte a execução do script, o autoload, geralmente, é a primeira coisa que fazemos. O __autoload é ruim pois você só poderá ter um autoload, se for trabalhar com bibliotecas de terceiros isto se torna ruim. Outra coisa é que conforme cresce o sistema, a pasta de classes ficará enorme, não manutenível, sendo assim, precisará de separar classes em pastas, hoje em dia, com o PHP 5.3, existem namespaces e elas podem ser muito úteis para organizar o código. Veja a SplClassLoader: https://gist.github.com/jwage/221634

 

3 - Sim e não. O acoplamento que isso gera é enorme, mesmo não precisando saber logo de agora, saiba que existem testes unitários, que por sinal são muito, mas muito importantes, e vai acontecer um problema quando você for testar essa classe: ela não é testável. Você trabalha com sessions globalmente (uma vez que as funções são globais) e com isso você não vai conseguir testar sua lógica, pois você depende de estado global. Se criar classes "wrappers" para as sessions e para as superglobais e injetá-las, você remove o estado global, pois pode, no momento do teste criar um mock (uma espécie de classe de mentira) e assim, conseguir testar traquilamente. Estado global é veneno.

 

Uma dica: não deixe seus métodos com mais de 3 parâmetros (exceção: construtor), muitas coisas você pode injetar pelo construtor.

Compartilhar este post


Link para o post
Compartilhar em outros sites

2 - Você sempre terá um arquivo de index.php, onde parte a execução do script, o autoload, geralmente, é a primeira coisa que fazemos. O __autoload é ruim pois você só poderá ter um autoload, se for trabalhar com bibliotecas de terceiros isto se torna ruim. Outra coisa é que conforme cresce o sistema, a pasta de classes ficará enorme, não manutenível, sendo assim, precisará de separar classes em pastas, hoje em dia, com o PHP 5.3, existem namespaces e elas podem ser muito úteis para organizar o código. Veja a SplClassLoader: https://gist.github.com/jwage/221634

 

É verdade, como estou fazendo este projeto de aprendizado de OO sem uma index que faça includes das outras páginas do sistema não me atentei ao fato de que num sistema real eu só precisaria incluir o autoload na index. Talvez eu deva deixar meu ambiente de estudos de OO mais parecido com um ambiente real para absorver melhor os conceitos.

 

Mas por exemplo, dentro do sistema em si eu posso ter uma index que inclua as outras páginas do sistema, e o autoload ficaria apenas na index. Mas e na página de login ? Eu teria que colocar o autoload novamente, ou então fazer o require_once de uma vez, mesmo que só nessa página.

 

 

3 - Sim e não. O acoplamento que isso gera é enorme, mesmo não precisando saber logo de agora, saiba que existem testes unitários, que por sinal são muito, mas muito importantes, e vai acontecer um problema quando você for testar essa classe: ela não é testável. Você trabalha com sessions globalmente (uma vez que as funções são globais) e com isso você não vai conseguir testar sua lógica, pois você depende de estado global. Se criar classes "wrappers" para as sessions e para as superglobais e injetá-las, você remove o estado global, pois pode, no momento do teste criar um mock (uma espécie de classe de mentira) e assim, conseguir testar traquilamente. Estado global é veneno.

 

Então se eu entendi, o motivo de uma classe para gerenciamento de sessão é mais para manter minhas classes testáveis ?

 

 

Uma dica: não deixe seus métodos com mais de 3 parâmetros (exceção: construtor), muitas coisas você pode injetar pelo construtor.

 

No caso do método validarUsuario eu não vejo outra forma. Além dos parâmetros óbvios como login e senha eu ainda preciso da instância PDO e da tabela na qual a consulta será realizada. Só se eu passar login e senha em um array.

Compartilhar este post


Link para o post
Compartilhar em outros sites

1 - Veja o que é um FrontController, esqueça essa de ter um arquivo para abrir no browser para cada página.

 

2 - Não fará sentido algum, você provavelmente não conhece testes unitários (não precisa sair aprendendo desesperadamente não rs), mas o ponto é que você encapsularia o estado global (o veneno :skull: ).

 

3 - Você pode passar a PDO no construtor, e usa o $this para acessá-la, quatro parâmetros em um método é dose. Evite arrays. Uma coisa simples, mas eficaz: cuidado, muito cuidado com o nome do método, validarUsuario deveria ser uma ação, pois o método possui um verbo, mas na verdade o método é uma verificação se o usuário é valido, logo o nome seria isUserValid (em pt seria algo como usuarioEValido, ficaria uma caca). Esse ponto é uma questão de semântica, não de lógica.

Compartilhar este post


Link para o post
Compartilhar em outros sites

1 - Veja o que é um FrontController, esqueça essa de ter um arquivo para abrir no browser para cada página.

 

Dei uma pesquisada rápida e se o exemplo que vi está correto, não é algo muito diferente do que costumo fazer. Pesquisarei mais amanhã. Mas o que eu faço em geral é ter uma index que pega na URL (não via GET) a página a ser acessada e faz um include dela.

 

3 - Você pode passar a PDO no construtor, e usa o $this para acessá-la, quatro parâmetros em um método é dose. Evite arrays. Uma coisa simples, mas eficaz: cuidado, muito cuidado com o nome do método, validarUsuario deveria ser uma ação, pois o método possui um verbo, mas na verdade o método é uma verificação se o usuário é valido, logo o nome seria isUserValid (em pt seria algo como usuarioEValido, ficaria uma caca). Esse ponto é uma questão de semântica, não de lógica.

 

O problema de passar o PDO no construtor da classe ControleLogin é que apenas um método o utiliza (pelo menos por enquanto). Só se eu colocar como parâmetro opcional. O que não sei é se existe alguma restrição teórica quanto a isso.

Compartilhar este post


Link para o post
Compartilhar em outros sites

1 - Você não vai ter arquivos públicos variados de PHP, apenas o index.php, não crie login.php, seilaoque.php para acessar páginas, além da URL ficar um lixo, você vai ter um descontrole de organização. Todas as requisições vão apontar para uma pasta onde haverá somente seus arquivos estáticos (js, css, img, swf, e arquivos públicos) e seu index.php, o resto é interno, baseado em classes, você terá um FrontController, que terá um Router de URL, que saberá como proceder.

 

Routers em PHP:

https://github.com/auraphp/Aura.Router

https://github.com/Respect/Rest

https://github.com/symfony/Routing

https://github.com/samwho/PHP-Routing

https://github.com/codeguy/Slim

 

3 - Não importa, isso é estado da classe. Veja um problema:

 

<?php

class Someclass
{
    public function doSomething(ControleLogin $login)
    {
        $login->validarUsuario('login', 'senha', $pdo, 'ffd'); // preciso passar um PDO :( difícil de implementar
    }
}

 

Classes não são repositório de funções, classes são protótipos de objetos, que podem conter estado. Veja o que poderia ser feito:

 

 

 

Fiz isso e alterei algumas coisas nas classes, se puderem me dizer se melhorou:

 

    class ControleLogin
    {
        private $login;
        private $senha;
        private $pdo;
        
        public function __construct(PDO $bd)
        {
            $this->pdo = $bd;
        }

        public function validarUsuario($login, $senha, $tabela)
        {   
            $this->login = $login;
            $this->senha = $this->gerarHash($senha);
            
            $qry = "SELECT COUNT(id) FROM {$tabela} "
                 . "WHERE login = :login AND senha = :senha AND status = '1'";
                 
            $stmt = $this->pdo->prepare($qry);
            $stmt->bindParam(':login', $this->login);
            $stmt->bindParam(':senha', $this->senha);
            $stmt->execute();
                
            if ($stmt->fetchColumn() == 1)
            {
                $this->gerarSessao();
                    
                return true;
            }
                
            return false;
        }
        
        public function logout()
        {
            session_start();
            
            $_SESSION = array();
            session_destroy();  
        }
        
        public function usuarioLogado()
        {
            session_start();
            
            return (isset($_SESSION['login'])) && (isset($_SESSION['validado']));
        }
        
        private function gerarHash($senha)
        {
            return sha1($senha);
        }
        
        private function gerarSessao()
        {
            session_start();
            
            $_SESSION['login'] =  $this->login;
            $_SESSION['validado'] = 1;
        }
    }

 

Mais coisas podem ser melhoradas, mas lembre-se: OO não é funcional, você tem estado de objeto, você tem como limpar parâmetros.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Enrico, eu fiz basicamente isso que você fez, mas meu construtor ficou assim:

 

public function __construct(PDO $bd = null)
{
    if ($bd != null)
    {    
        $this->pdo = $bd;
    }
}

 

Eu entendo o que vc diz. Foi justamente a dificuldade de implementar que me fez chegar às conclusões que cheguei. Mas o mais problema no momento é o seguinte:

 

public function usuarioLogado()
{
    session_start();
           
    return (isset($_SESSION['login'])) && (isset($_SESSION['validado']));
}

 

Essa é a função que eu chamo nas páginas restritas, e dependendo de seu retorno o usuário é redirecionado para a página de login. Para utilizar isso, na hora de instanciar o objeto eu teria que instanciar e passar um PDO sendo que isso não faz o menor sentido no contexto desse método. Por isso coloquei o PDO como opcional no construtor. Talvez seja um problema de projeto da classe, mas não sei como resolvê-lo.

 

Uma coisa que está me preocupando um pouco é o seguinte:

 

class UsuarioMapper
{
        
        public function incluir(Usuario $usuario)
        {
            $qry = "INSERT INTO {$this->tabela}(id, usuario, senha, email, nome, sobrenome, status, data_cadastro) "
                 . "VALUES(null, :login, :senha, :email, :nome, :sobrenome, '1', CURDATE())";
            
            try
            {
                $stmt = $this->bd->prepare($qry);
                $stmt->bindParam(':login', $usuario->getLogin());
                $stmt->bindParam(':senha', $usuario->getSenha());
                $stmt->bindParam(':email', $usuario->getSenha());
                $stmt->bindParam(':nome', $usuario->getNome());
                $stmt->bindParam(':sobrenome', $usuario->getSobrenome());
                $stmt->execute();
                
                $ultimoId = $this->bd->lastInsertId();
                $usuario->setId($ultimoId);
                
                return true;
            }
            catch (PDOException $e)
            {
                echo $e->getMessage();
                
                return false;
            }     
        }

 

As minhas consultas SQL estão fixadas no código da classe Mapper, não sei se isso é correto. Fico pensando na reutilização disso. Talvez fosse melhor passar as consultas como parâmetro ? Mas por outro lado isso aumenta a complexidade na utilização da classe. Não sei, talvez eu esteja meio paranoico quando aos conceitos de manter as coisas simples, reutilização e etc.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Para que o default? Na verdade, você tem problemas de design no código. é necessária uma abstração maior.

 

Seus métodos devem fazer algo. Esqueça essa de colocar parâmetro para tudo. Reutilização, em Orientação a Objetos, é basicamente a abstração. É necessário o conhecimento de Object-Oriented Design (princípios S.O.L.I.D. e conceitos) e o principal: o conhecimento do mundo real orientado a objetos, para isso, a melhor coisa é ler código, mas um bom código. Aqui no fórum, no tópico de GOF, existem códigos excelentes implementando patterns.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Para que o default? Na verdade, você tem problemas de design no código. é necessária uma abstração maior.

 

Seus métodos devem fazer algo. Esqueça essa de colocar parâmetro para tudo. Reutilização, em Orientação a Objetos, é basicamente a abstração. É necessário o conhecimento de Object-Oriented Design (princípios S.O.L.I.D. e conceitos) e o principal: o conhecimento do mundo real orientado a objetos, para isso, a melhor coisa é ler código, mas um bom código. Aqui no fórum, no tópico de GOF, existem códigos excelentes implementando patterns.

 

Exatamente pelo que eu expliquei: Se ele não for um parâmetro opcional eu vou ser obrigado a passar um objeto PDO para instanciar a classe. Só que o método usuarioLogado não necessita de conexão ao banco de dados. Pra que eu instanciaria um PDO e passaria como parâmetro pra utilizar esse método ? Por isso coloquei o default.

 

Eu li um do tópico de GOF. Mas o problema é o seguinte: Como o João Batista Neto falou num hangout sobre OO, a melhor forma de entender um problema, é ver esse problema. Os patterns adotados no tópico referido surgiram para solucionar problemas de abstração em sua maioria. Se eu pular diretamente pro estudo desses conceitos todos eu posso até passar a implementá-los com sucesso, mas eu nunca vou saber exatamente porque estou fazendo daquela forma, já que eu não conheci o problema a fundo.

 

Por isso eu abri este tópico, eu quero aprender OO do início, com os conceitos mais básicos. A melhor forma que eu vi para isso foi começar a fazer e dar a cara a tapa aqui, receber críticas. Quando eu me deparar com determinados problemas vai ser a hora de conhecer os patterns mais avançados e entendê-los de fato.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Você caiu em um problema de design. Você violou o S.R.P., dedique uma hora, no mínimo para ver realmente os 5 princípios de design de orientação a objetos. Alguns links (quase todos slides):

 

http://en.wikipedia.org/wiki/SOLID_(object-oriented_design)

http://www.infoq.com/br/presentations/principios-solid

http://www.slideshare.net/bbossola/geecon09-solid

http://www.slideshare.net/jcfischer/solid-ruby-solid-rails

http://www.slideshare.net/viniciusquaiato/orientao-a-objetos-princpios-solid

http://www.slideshare.net/EdmilsonFilho2/princpios-solid-12117410

http://www.slideshare.net/martinlippert/why-solid-matters-even-for-javascript

 

 

Apesar da mistura de linguagens (e não ter nenhum em PHP :( ), ele demonstra o conceito, esses 5 princípios são fundamentais. E existe muito material pela internet.

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.