Ir para conteúdo

POWERED BY:

Arquivado

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

Thomas Piedade

Poo + DAO

Recommended Posts

Boa tarde amigos!

 

 

Pois bem, trabalho com php faz uns 4 anos e "achei" que sabia POO, rs.

 

Porém, há algumas semanas comecei a frequentar o fórum e notei que não sei é nada...minhas classes são um "monte" de funções aglomeradas.

 

Estou começando a montar um sistema de cadastro/login de usuários e estou com algumas dúvidas.

 

Bom vamos lá:

 

tenho as seguintes classes:

 

class/Users.php

<?php
class User{			
	private $user_id;
	private $nome;
	private $email;
	private $senha;	
	
	// Geters and Seters
	public function setIdUser($user_id){
		$this->user_id = $user_id;
	}
	public function getIdUser(){
		return $this->user_id;
	}

	public function setUserNome($nome){
		$this->nome = $nome;
	}
	public function getUserNome(){
		return $this->nome;
	}
	public function setUserEmail($email){
		$this->email = $email;
	}
	public function getUserEmail(){
		return $this->email;
	}

	public function setUserSenha($senha){
		$this->senha = $senha;
	}
	public function getUserSenha(){
		return $this->senha;
	}	
}

dao/UsersDAO.php

class UserDAO{
	private $pdo;	
	public function __construct($pdo){	
		$this->pdo = $pdo;	
		$this->pdo->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );
	}	
	public function insert(User $usuario){		
		$sql = $this->pdo->prepare('INSERT INTO usuarios (nome, email, senha) VALUES (:nome, :email, :senha)');
		$sql->bindValue(':nome' , $usuario->getUserNome());
		$sql->bindValue(':email', $usuario->getUserEmail());
		$sql->bindValue(':senha', md5($usuario->getUserSenha()));
		if ($sql->execute()){
    		// Query succeeded.
    		return true;
		}else{
    		// Query failed.
   			echo $sql->errorCode();
		}
	}
}
por enquanto só tem o insert ..rs

 

 

Minha index.php pra teste:

<?php
//Class includes
include ('class/User.php');
include ('dao/UserDAO.php');

//Conexão com Banco de dados
$conexao = new PDO('mysql:host=localhost;dbname=oop','root','123456');
$userDAO = new UserDAO($conexao);
$user    = new User;

$user->setUserNome('Thomas Piedade');
$user->setUserEmail('thomas@email.com.br');
$user->setUserSenha('123456');

if($userDAO->insert($user))echo 'Usuario inserido com sucesso!';

 

Minha duvida é a seguinte...

 

Pra eu fazer a validação de login do usuário sem quebrar o conceito de SRP, eu tenho que criar uma classe de validação? Ou posso fazer a validação na própria UsersDAO?

 

 

Esse é só o começo ...rs. Mas eu comecei já com essa dúvida!

 

 

Abraços.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Validação do login na classe DAO viola o principio SRP. Crie uma classe especifica. Você pode passar o objeto Dao por injeção de dependência para checar o usuário no banco de dados.

Compartilhar este post


Link para o post
Compartilhar em outros sites

 

// Geters and Seters

 

Também conhecidos como vômitos de IDEs. Na maioria dos casos setters são desnecessários e/ou getters podem ser substituídos por métodos que possam demonstrar o estado de forma menos primitiva.

 

Nesse caso, os setters são desnecessários. Não há necessidade de mutabilidade nesse caso, já que um objeto usuário não muda. Você poderia simplificar e tornar o objeto em um simples VO usando um construtor e removendo os setters:

 

 

<?php
class User{			
	private $user_id;
	private $nome;
	private $email;
	private $senha;	

        public function __construct($user_id, $nome, $email, $senha){
            $this->user_id = $user_id;
            $this->nome = $nome;
            $this->email = $email;
            $this->senha = $senha;
       }

	public function getIdUser(){
		return $this->user_id;
	}

	public function getUserNome(){
		return $this->nome;
	}

	public function getUserEmail(){
		return $this->email;
	}

	public function getUserSenha(){
		return $this->senha;
	}	
}

 

 

$this->pdo->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );

 

Nâ na ni na não! Não é responsabilidade da DAO fazer configuração. A PDO deve ser configurada antes de injetar.

 

 

$sql->bindValue(':senha', md5($usuario->getUserSenha()));

 

Nada relacionado com OO, mas md5? MD5 está "quebrado" desde a década de 90. SHA-256/512 e Bcrypt são algumas alternativas.

 

 

		if ($sql->execute()){
    		// Query succeeded.
    		return true;
		}else{
    		// Query failed.
   			echo $sql->errorCode();
		}

 

homer_doh.thumbnail.jpg

 

Echo reduz imensamente a capacidade de reutilização e aumenta calamitosamente as chances de um "Headers already sent". Return true/false é um método bem rudimentar de tratar erros. No PHP, assim como na maioria das linguagens OOP, há suporte para exceptions.

 

 

//Class includes
include ('class/User.php');
include ('dao/UserDAO.php');

 

Autoloading e PSR-0 são os remédios adequados para este problema.

 

Pra eu fazer a validação de login do usuário sem quebrar o conceito de SRP, eu tenho que criar uma classe de validação? Ou posso fazer a validação na própria UsersDAO?

 

O propósito da DAO é apenas fazer persistência de dados. Login no caso não seria muito bem uma validação, mas sim uma lógica que pode ser implementada com ACL. O Symfony tem um componente bem grande que resolve isso: http://symfony.com/doc/current/cookbook/security/index.html

Compartilhar este post


Link para o post
Compartilhar em outros sites

Muito Obrigado pelas correções Enrico, isso só vem a agregar ao conhecimento.

 

- O echo no metodo era só pra teste mesmo(sistema é só isso por enquanto..rs), mas pretendo ver alguma alternativa.

 

- Esse conceito de autoloading ainda não entrou na minha cabeça, não consigo entender a forma correta! Mas pretendo estudar isso ainda.

 

É aquele negócio... Estou primeiro tentando entender o conceito e depois ir acertando o resto. Muita informação hehuhaeuha

 

Obrigado a todos! Já fiz algumas mudanças aqui e logo mais posto aqui!

 

 

##########################################

 

 

 

Então mudei bastante coisas rss.

 

 

 

index.php

 

<?php
//Class includes
include ('User.php');
include ('dao/MySQLStorage.php');
include ('dao/UserStorage.php');

//Conexão com Banco de dados
$conexao 	 = new PDO('mysql:host=localhost;dbname=oop','root','');

//Instances
$mysqlStorage= new MySQLStorage($conexao);
$userStorage = new UserStorage($mysqlStorage);
$user        = new User('Thomas Piedade','thomas@email.com.br','123456');
$userStorage->insert($user);

User.php

<?php
class User{		
	private $user_id;// Essa propriedade seria necessaria aqui? É uma chave primária no BANCO
	private $nome;
	private $email;
	private $senha;	
		
	public function __construct($nome, $email, $senha){
        $this->nome    = $nome;
        $this->email   = $email;
        $this->senha   = $senha;
    }	
	public function getIdUser(){
		return $this->user_id;
	}
	public function getUserNome(){
		return $this->nome;
	}
	public function getUserEmail(){
		return $this->email;
	}
	public function getUserSenha(){
		return $this->senha;
	}	
}

UserStorage.php

<?php
class UserStorage{
	private $table = 'usuarios';
	private $storage;	
	public function __construct(iStorage $storage){			
		$this->storage = $storage;
	}	
	public function insert(User $user){		
		$data['nome']  = $user->getUserNome();
		$data['email'] = $user->getUserEmail();
		$data['senha'] = hash('sha512', $user->getUserSenha());		
		$this->storage->insert($this->table, $data);		
		//$user->setIdUser($this->storage->getInsertId());			
	}
}

iStorage.php

<?php
interface iStorage{
	public function setInsertId($id);
		
	public function getInsertId();

	public function insert($table, array $data);	
}

MySQLStorage.php

 

<?php
require_once('dao/iStorage.php');

class MySQLStorage implements iStorage{
	private $pdo;
	public $insert_id;
	public $error;
	public function __construct($conn){	
		$this->pdo = $conn;	
		//$this->pdo->setAttribute( PDO::ATTR_ERRMODE , PDO::ERRMODE_EXCEPTION); // Onde faço essa configuração? Na propria instancia na index??
	}
	
	public function setInsertId($id){
		$this->insert_id = $id;
	}
		
	public function getInsertId(){
		return $this->insert_id;
	}
			
	public function insert($table, array $data){		
		foreach($data as $field => $value){
			$fields[] = $field;
			$values[] = $value; 
		}
		$bindvalues = ':' . implode(', :',$fields);				
		$fields 	= implode(', ',$fields);							
		$sth = $this->pdo->prepare('INSERT INTO ' . $table . ' (' . $fields . ') VALUES (' . $bindvalues . ')');
		foreach ($data as $f => $v){
			$sth->bindValue(':' . $f, $v);
		}
		if($sth->execute()){	
			$this->setInsertId($this->pdo->lastInsertId());
			return true;
		}else{
			$this->error = $sth->getMessage();
		}		
	} 
}

Criei uma interface para storage não sei se esta correta, pois não sabemos futuramente se precisaremos alterar de banco.

 

Fiz algumas aterações de acordo com as sugestões.

 

O retorno do erro ainda não tive uma ideia..fiz uma mudança besta nele rs

 

Amanhã vejo se consigo fazer a classe de autenticação.

 

 

Abs

Compartilhar este post


Link para o post
Compartilhar em outros sites

//$this->pdo->setAttribute( PDO::ATTR_ERRMODE , PDO::ERRMODE_EXCEPTION); // Onde faço essa configuração? Na propria instancia na index??

Sim.

 

O retorno do erro ainda não tive uma ideia..fiz uma mudança besta nele rs

 

Use try/catch para os erros.

 

foreach ($data as $f => $v){
	$sth->bindValue(':' . $f, $v);
}

Não precisa disso, você pode usar o próprio PDOStatement::execute() pra isso, deixa um código mais limpo:

try{
    $values = implode( '?, ', array_values( $data ) );
    $fields = '`' . implode( '`, `', array_keys( $keys ) ) . '`';

    $sql = "INSERT INTO `{$table}`
        ({$fields})
        VALUES ({$values})";

    $query = $this->pdo->prepare( $sql );
    $query->execute( array_values( $data ) );
}catch( \PDOException $e ){
    echo $e->getMessage();
}

Compartilhar este post


Link para o post
Compartilhar em outros sites

Usar o "user" nos nomes dos atributos e métodos da classe me parece redundante, geralmente é bom fazer isso na modelagem das tabelas do banco de dados mas não em uma classe.

 

Na classe User, você poderia usar apenas getId() em vez de getIdUser(), o mesmo para os outros métodos e usar apenas $id em vez de $user_id.

 

Isso não é algo realmente importante mas é bom diminuir os nomes dos métodos e atributos até o ponto em que não prejudique o entendimento de outro programador.

 

Outro detalhe é o idioma, por exemplo getUserSenha(), getUserNome() estão uma mistura de inglês e português. Via de regra use 100% inglês.

 

Sua classe MySQLStorage tem alguns problemas, por exemplo não vejo motivo para nome ligado ao MySql, comentaria mais sobre ela mas minhas classes de persistência sempre foram ruins então vou deixar para alguém com mais experiencia.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Entendi Raoni, os nomes dos métodos estão uma mistureba mesmo heuaheuha.

 

Vou seguir sua ideia e vou alterá-los..

 

É que antes chamavam getUsuarioNome(), getUsuarioSenha, porem achei que estavam muito extensos e acabei alterando..hehe

 

Segue a classe alterar...bem mais clean né? rs

 

<?php
class User{		
	private $id;// Essa propriedade seria necessaria aqui? É uma chave primária no BANCO
	private $name;
	private $email;
	private $password;	
		
	public function __construct($name, $email, $password){
        $this->name    = $name;
        $this->email   = $email;
        $this->password= $password;
    }	
	public function getId(){
		return $this->id;
	}
	public function getName(){
		return $this->name;
	}
	public function getEmail(){
		return $this->email;
	}
	public function getPassword(){
		return $this->password;
	}	
}

 

Sobre os métodos de persistência, realmente não são dos melhores...ainda me falta um pouco de conhecimento, principalmente em PDO que comecei a usar a pouco tempo;

 

 

Abraços.

Compartilhar este post


Link para o post
Compartilhar em outros sites
Gente criei uma classe de autenticação.. Podem fazer uma avaliação???
kk Isso dentro dos meus conhecimentos...
O que pode ser melhorado??
<?php
class UserAuth{
	/* @var $pdo 
	   Recebe uma instancia da conexão em PDO
	*/
	private $pdo;//PDO instance
	/* @var $user  array
	  Recebe dados do usuario ao logar
	*/
	private $user;
	 
	public function __construct($pdo){	
		$this->pdo = $pdo;	
	}
	
	public function login($redirect = NULL){	
		$this->setSession(array( 'user' => $this->user ));
		if( $redirect !== NULL ) $this->redirect($redirect);		
	}
	
	public function validate($email, $password){
		$sql = 'SELECT * FROM usuarios WHERE email = :email AND senha = :password';		
		$sth = $this->pdo->prepare($sql);
		$sth->bindValue(':email'   , $email);
		$sth->bindValue(':password', hash('sha512',$password));
		$sth->execute();
		$this->user = $sth->fetch();
		return $sth->rowCount();
	}
	
	public function getUser(){
		return $this->user;
	}
		
	public function exists($field, $value){
		$sql = 'SELECT * FROM usuarios WHERE ' . $field . ' = :' . $field;
		$sth = $this->pdo->prepare($sql);
		$sth->bindValue(':' . $field , $value);
		$sth->execute();
		return count($sth->fetchAll());		
	}
		
	public function setSession($data){
		$_SESSION = $data;	
	}
		
	public function isSetSession($session){
		return isset($session);
	}
		
	public function redirect($url){
		header('Location: ' .  $url);
	}	
	
	public function logout($redirect = NULL){
		if(isset($_SESSION)){
			unset($_SESSION);
			session_destroy();
		}		
		if( $redirect !== NULL ) $this->redirect($redirect);
	}
}

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

Observando rapidamente:

 

- autenticação está acoplada com o banco de dados (PDO). Não importa de onde virão as informações, isso é irrelevante em OOP -> abstração

- autenticação está acoplada com session/HTTP, o mesmo que disse acima

- não é responsabilidade de uma classe de autenticação fazer redirecionamentos nem fazer manipulação de session

- poderia ser reutilizável se não fosse específica para um usuário

Compartilhar este post


Link para o post
Compartilhar em outros sites

Esse enrico manja muito! ahuahuah

 

Mas então, nem sei como fazer, já pesquisei muito sobre sistema de login usando POO e nunca acho nada usando POO verdadeiro :(

 

Eu teria que fazer uma classe pra sessão?

 

Eu teria que fazer uma classe pra redirecionamento?

 

Sobre o pdo eu tinha pensado realmente nisso, mas como ainda não tinha criado um metodo select na minha Storage fiz assim mesmo.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Cara, olha o meu sistema: http://xurisso.amserver1.com/xurisso1

 

 

Se tu quiser eu te passo ele, com a condicao de me enviar o que você aperfeiçoar nele.

só mandar e-mail para xurissoooo@gmail.com solicitando

 

 

Assim você pode fazer seu sistema do zero se baseando no meu, que os codigos estão acho eu, no conceito que você quer.

 

E eu estou com uns probleminhas em seguir em frente com ele em algumas funções novas lá

Compartilhar este post


Link para o post
Compartilhar em outros sites

Pense um bocadinho, que não faz muito mal: qual é o protocolo que nós usamos para trafegar dados na web? Esse protocolo é baseado no quê?

 

Tendo isso em mente, nós podemos facilmente criar um pacote HTTP (o protocolo) e incluir as classes que tratarão Requests e Responses (a estrutura mais básica do HTTP).

 

Indo mais longe, uma request, do ponto de vista do servidor, é o que o usuário manda. No PHP, usamos as superglobais $_GET, $_POST, $_SERVER, etc. Além disso, uma request é sempre imutável, basta perceber que nós nunca criamos/editamos algo nessas superglobais. Tratar uma request seria facil então, com um simples VO:

<?php

namespace Http;

class Request {
    private $get;
    private $post;
    private $server;

    // passamos os dados por parâmetros para reduzir o acoplamento e aumentar a flexibilidade
    public function __construct(array $get, array $post, array $server) {
        $this->get = $get;
        $this->post = $post;
        $this->server = $server;
        // e assim vai, do jeito que preferir
    }

    public function getQuery($queryString) {
        return $this->get[$queryString];
    }

    public function getPost($post) {
        return $this->post[$post];
    }

    public function getServer($server) {
        return $this->server[$server];
    }
}

$request = new Request($_GET, $_POST, $_SERVER);

// um exemplo da flexibilidade que ganhamos: é possível criar uma request falsa para testes, por exemplo
$fakeRequest = new Request(['page' => 3], [], ['PATH_INFO' => '/foo/bar/baz']);

Já a response, o PHP fornece apenas funções (echo, header, setcookie) para enviar a resposta para o cliente. O problema disso é que causa facilmente o "Headers already sent" e deixa o código impossível de ser testado em isolamento. Para isso, podemos criar uma interface que define o comportamento de uma response:

<?php

namespace Http;

interface ResponseSender {
    public function send(Response $response);
}

E criaríamos um VO para a response:

<?php

namespace Http;

class Response {
    private $version;
    private $statusCode;
    private $reasonPhrase;
    private $headers;
    private $content;

    // as reason phrases padrão do HTTP
    private $recommendedReasonPhrases = [
        200 => 'OK',
        // ...
    ];

    public function __construct($statusCode, $content, array $headers = [], $version = 1.1, $customReasonPhrase = '') {
        $this->statusCode = $statusCode;
        $this->content = $content;
        $this->headers = $headers;
        $this->version = $version;

        if (! empty($customReasonPhrase)) {
            $this->reasonPhrase = $customReasonPhrase;
        } elseif (isset($this->recommendedReasonPhrases[$statusCode])) {
            $this->reasonPhrase = $this->recommendedReasonPhrases[$statusCode];
        } else {
            throw new Exception('No recommended reason phrase for status code ' . $statusCode);
        }
    }

    public function getHttpVersion() { return $this->version; }
    public function getStatusCode() { return $this->statusCode; }
    public function getReasonPhrase() { return $this->reasonPhrase; }
    public function getHeaders() { return $this->headers; }
    
    public function getHeader($name) {
        if (! isset($this->headers[$name])) {
            throw new Exception('Undefined header ' . $name);
        }

        return $this->headers[$name];
    }

    public function getContent() { return $this->content; }

    public function __toString() {
       $string = 'HTTP/' . $this->version . ' ' . $this->statusCode . ' ' . $this->reasonPhrase . "\n";
       
       foreach ($this->headers as $name => $value) {
           if (is_array($value)) {
               foreach ($value as $subvalue) {
                   $string .= $name . ': ' . $subvalue . "\n";
               }
           } else {
               $string .= $name . ': ' . $value . "\n";
           }
       }

       $string .= "\r\n";
       $string .= $this->content;

       return $string;
   }
}

E aí eu acho que você pode ter uma ideia de como isso pode ser feito.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Vish mano!!!

 

Que viagem isso ai auhauhua, eu estou apenas começando com poo mas vamos lá!

 

Vou tentar pensar dessa maneira..

 

No caso dessa classe Request, session entraria nela também??

 

Você já tem um pensamento muito avançado com OOP hehe.. Difícil entender os benefícios de uma classe apenas pra isso.

 

Quais seriam eles?

 

Abraços e muito obrigado por perder seu tempo kk

Compartilhar este post


Link para o post
Compartilhar em outros sites

No caso dessa classe Request, session entraria nela também??

 

Session não é uma request, acho que você esta confundindo porque é uma variavel global do PHP. =)

 

 

Você já tem um pensamento muito avançado com OOP hehe.. Difícil entender os benefícios de uma classe apenas pra isso.

 

Quais seriam eles?

 

Ele tem mesmo né? Rs

Mas não é tão dificil entender...

 

Um dos beneficios de se utilizar classes assim é um que ele mesmo já citou:

 

 

 

// um exemplo da flexibilidade que ganhamos: é possível criar uma request falsa para testes, por exemplo
$fakeRequest = new Request(['page' => 3], [], ['PATH_INFO' => '/foo/bar/baz']);

Isso é ótimo para testes.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Claro que pode, mas estão bem incompletas. O Symfony\HttpFoundation tem uma implementação dessas bem completa.

 

Você já tem um pensamento muito avançado com OOP hehe.. Difícil entender os benefícios de uma classe apenas pra isso.

 

Isso é um pouco de lógica básica. Linguagens orientadas a objeto têm por padrão esse tipo de coisa. É que o PHP é gambiarra-oriented.

 

No caso dessa classe Request, session entraria nela também??

 

Não. Sessions não fazem parte do Http, elas apenas se comunicam com o HTTP através de um cookie.

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.