Usamos cookies para medir audiência e melhorar sua experiência. Você pode aceitar ou recusar a qualquer momento. Veja sobre o iMasters.
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;
}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();
}
}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.
Opa, vou fazer isso sim!
Estou mudando algumas coisas mas ai posto novamente pra galera mais experiente dar uma olhada!
Obrigado!
>
// 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();
}
/applications/core/interface/imageproxy/imageproxy.php?img=http://www.cfchimp.com/wordpress/wp-content/uploads/2008/03/homer_doh.thumbnail.jpg&key=784c6f379df13b99cd13ead07de77ec0d0fd380168796d1424003e04d8ad84f3" alt="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
Muito Obrigado pelas correções Enrico, isso só vem a agregar ao conhecimento.
É 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);<?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;
}
}<?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());
}
}<?php
interface iStorage{
public function setInsertId($id);
public function getInsertId();
public function insert($table, array $data);
}<?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();
}
}
}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
//$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 ) ); echo $e->getMessage();
}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.
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.
Boa tarde,
Alguém teria algum exemplo de classe de login usando ACL sem utilização de frameworks??
Não sou familiarizado com nenhum.
Obrigado desde já.
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);
}
}Observando rapidamente:
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.
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á
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']);<?php
namespace Http;
interface ResponseSender {
public function send(Response $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.
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
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.
SIm entendi! Pra teste facilita bem,
Ainda estou analisando ahuahu
Outra coisa:
Ficará mais fácil de você fazer filtros nos valores vindo da request =)
Enrico, posso usar essas classes que você postou e alterar de acordo com minha necessidade?
É apenas um sistema pra estudos..
Abs
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.
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.