Superclasses, Interfaces e Orientação a Objetos no PHP

Basicamente o nosso exemplo será uma pequena aplicação para persistir objetos, no caso as operações basicas (CRUD). Começamos pela nossa superclasse comum para o modelo de negócio do nosso sistema:

A classe Bean contém as características básicas para um objeto do sistema.

[php]
abstract class Bean
{
protected $id = 0;
protected $tablename;

public function Bean($tablename)
{
if(!$tablename)
{
die(‘Por favor informe o nome da tabela do objeto.’);
}
$this->set_tablename($tablename);
}

public function get_id () { return $this->id; }
public function get_tablename () { return $this->tablename; }
public function set_id ($id) { $this->id = $id; }
public function set_tablename ($tablename) { $this->tablename = $tablename; }
}
?>
[/php]

A seguir temos a classe Usuario, ou melhor chamarmos do nosso Objeto Usuario. O mesmo estende as características da superclasse Bean, assim garante um comportamento padrão para todos os objetos do sistema.

[php]
<?php
require_once (dirname(__FILE__).&quot;/Bean.class.php&quot;);

class Usuario extends Bean
{
private $nome;
private $senha;
private $login;

// getters e setters
public function get_nome () { return $this->nome; }
public function get_senha () { return $this->senha; }
public function get_login () { return $this->login; }
public function set_nome ($nome){ $this-&gt;nome = $nome;}
public function set_login ($login){ $this->login = $login;}
public function set_senha ($senha){ $this->senha = $senha;}
}?>
[/php]

Basicamente todas os outros objetos do sistema seguiriam este padrão de utilização, as necessidades podem mudar de acordo com o projeto que você esteja desenvolvendo.

Agora vamos imaginar uma peça que iremos fazer para persistir os nossos objetos de maneira generica por exemplo, para isso temos que pensar em algumas coisas, como:

  • Ele irá ser 100% funcional para todos os casos;
  • Que tipos de regras terei que seguir para utiliza-lo;
  • Tipos de resposta;
  • Performance;
  • Escalabilidade;
  • Viabilidade se comparado ao uso de algum framework;
  • Etc;

Sempre surgem muitas perguntas quanto a isso, mas vamos pensar no seguinte, se você precisa desenvolver um componente que será utilizado frequentemente no seu sistema, tenha sempre em mente que as regras de negócio podem ser alteradas por algum motivo X, Y ou Z, tente sempre se previnir de alguma maneira, tanto na análise do seu projeto quanto no código que você ou a sua equipe já tenham martelado.

Vamos montar um componente para servir de modelo para os seus objetos de persistencia do nosso exemplo.

Eu sempre sigo um meio que me previne de alterações inesperadas, como assim? Quando desenvolvo a camada de negócio, sempre faço uma classe que contenha uma implementação genérica que sirva aos elementos básicos da aplicação tais como categorias, tipos, marcadores, etc.
Vamos construir a nossa Interface para padronizar os métodos comuns do nosso modelo de negócio.

[php]
<?php
require_once (dirname(__FILE__).”/Bean.class.php”);

interface IObjectManager
{
public function save (Bean $object, $tablename, $return_mode = &quot;OBJECT&quot;);
public function delete(Bean $object, $return_mode = &quot;OBJECT&quot;);
public function get (Bean $object);
public function get_list (Bean $object, $params);
}?>
[/php]

A Interface IObjectManager define todos os métodos comuns para os objetos gerenciadores da regra de negócio, em miúdos, as lógicas de inserções, exclusões, etc. Nesta interface já podemos notar a tipagem dos parâmetros na assinatura dos métodos comuns a todas as classes que implementarem esta interface.

Agora vamos a nossa classe que contém uma implementação genérica para a nossa camada de persistência, mas antes ainda vamos ver a nossa classe para criar as conexões com a base de dados MySQL.

[php]
<?php
class MySQL
{
private $conn = null;
private $link = null;
private $result = null;
private $error  = null;

public function MySQL()
{
if(!$this->conn)
{
$this->conn = mysql_connect(“localhost”, “root”, “123”);
$this->link = mysql_select_db(“meubanco”, $this->conn);
}
}

public function __destruct()
{
if($this->has_connection())
{
$this->close_connection();
}
}

protected function has_connection ()
{
return $this->conn ? true : false;
}
protected function execute_query ($query)
{
$this->result = mysql_query($query, $this->conn) or $this->error = mysql_error($this->conn);
}
protected function get_affected_rows ()
{
return mysql_affected_rows($this->conn);
}
protected function get_error_message ()
{
return $this->error;
}
protected function close_connection ()
{
return mysql_close ($this->conn);
}
}
?>
[/php]

Vamos ficar atentos ao seguinte item interessante e muito bacana que o PHP nos disponibiliza, o método mágico “__destruct()”, ou seja, este sempre será executado quando uma classe destruída da memória ou do seu ciclo de execução na aplicação. Neste nosso tutorial eu preferi apenas destruir a conexão com a base de dados.

Agora vamos a nossa implementação genérica, que é claro não vou deixar totalmente feita, mas apenas vai servir de exemplo para quem estiver querendo entender como que funciona a Orientação a Objetos no PHP.

[php]
<?php
require_once (dirname(__FILE__).&quot;/MySQL.class.php&quot;);
require_once (dirname(__FILE__).&quot;/IObjectManager.class.php&quot;);

class ObjectManager extends MySQL implements IObjectManager
{
public function ObjectManager()
{
}

public function save (Bean $object, $tablename, $return_mode = &quot;OBJECT&quot;)
{
return $object;
}

public function delete (Bean $object, $return_mode = &quot;OBJECT&quot;)
{
$ob = $this->get($object);

if($ob)
{
$query = “DELETE FROM “.$object->get_tablename().” WHERE id = ‘”.$object->get_id().”‘”;

$this->execute_query($query);

return $ob;
}
else
{
return null;
}
}

public function get (Bean $object)
{

}

public function get_list(Bean $object, $params)
{

}
?>
[/php]

Na classe acima temos uma “implementação” genérica para atender a maioria dos casos, mas é claro que será necessário escrevermos classes especializadas para atender casos especificos, como o login de usuários no sistema.
No código abaixo temos uma classe que estende da interface e apenas implementa um novo método, o login_usuario.

[php]
<?php
require_once (dirname(__FILE__).”/MySQL.class.php”);
require_once (dirname(__FILE__).”/IObjectManager.class.php”);

class UsuarioManager extends MySQL implements IObjectManager
{
function UsuarioManager()
{
}

public function login_usuario ($login, $senha)
{
$query = “SELECT u.* FROM usuario u WHERE u.login = ‘$login’ AND u.senha = PASSWORD(‘$senha’)”;

$this->execute_query($query);

return $this-&gt;get_result();
}

public function save (Bean $object, $tablename, $return_mode = &quot;OBJECT&quot;)
{
// não necessário
}

public function delete (Bean $object, $return_mode = &quot;OBJECT&quot;)
{
// não necessário
}

public function get     (Bean $object)
{
// não necessário
}

public function get_list(Bean $object, $params)
{
// não necessário
}
}
?>
[/php]

Este não seria um modo ideal de se fazer, porque temos diversos “métodos perdidos e não implementados” por nada, poderiamos fazer uma extensão da classe ObjectManager que já está toda implementada e caso necessitarmos de algum método básico, o mesmo já estará implementado.

No código da classe UsuarioManagerImpl, apenas estendi as funcionalidades da classe genérica ObjectManger e obtive o mesmo resultado da classe UsuarioManager só que com muito menos código.

[php]
<?php
require_once (dirname(__FILE__).”/MySQL.class.php”);
require_once (dirname(__FILE__).”/IObjectManager.class.php”);

class UsuarioManagerImpl extends ObjectManager
{
public function login_usuario ($login, $senha)
{
$query = “SELECT u.* FROM usuario u WHERE u.login = ‘$login’ AND u.senha = PASSWORD(‘$senha’)”;

$this->execute_query($query);

return $this->get_result();
}
}
?>
[/php]

Espero que tenha sido útil este pequeno tutorial, abaixo segue o arquivo do tutorial em anexo para quem desejar baixar.

Qualquer dúvida é só postar.