1
I’m reading a POO book in PHP that calls PHP Object Oriented Programming [Pablo Dall'Oglio] and I was a little confused with these two Patterns design, especially when it makes use of Repository using Activerecord. It is worth mentioning that in the examples there is no use of namespace, prepare (SQL Injection), because this chapter of the book has not been dealt with yet.
<?php
abstract class Record
{
protected $data; //Array contendo os dados do objeto
public function __construct($id = NULL)
{
if ($id) { //Se o ID for informado
//Carrega o objeto correspondente
$object = $this->load($id);
if ($object) {
$this->fromArray($object->toArray());
}
}
}
public function __clone()
{
unset($this->data['id']);
}
public function __set($prop, $value)
{
if (method_exists($this, 'set_'.$prop)) {
//Executa o método set_<propriedade>
call_user_func(array($this, 'set_'.$prop), $value);
} else {
if ($value === NULL) {
unset($this->data[$prop]);
} else {
$this->data[$prop] = $value;
}
}
}
public function __get($prop)
{
if (method_exists($this, 'get_'.$prop)) {
//Executa o método get_<propriedade>
return call_user_func(array($this, 'get_'.$prop));
} else {
if(isset($this->data[$prop])) {
return $this->data[$prop];
}
}
}
public function __isset($prop)
{
return isset($this->data[$prop]);
}
private function getEntity()
{
$class = get_class($this); //Obtém o nome da classe
return constant("{$class}::TABLENAME"); //Retorna a constante de classe TABLENAME
}
public function fromArray($data)
{
$this->data = $data;
}
public function toArray()
{
return $this->data;
}
public function store()
{
$prepared = $this->prepare($this->data);
//Verifica se tem ID ou se existe na base de dados
if (empty($this->data['id']) or (!$this->load($this->id))) {
//Incrementa o ID
if (empty($this->data['id'])) {
$this->id = $this->getLast() + 1;
$prepared['id'] = $this->id;
}
//Cria uma instrução de insert
$sql = "INSERT INTO {$this->getEntity()} " . '(' . implode(', ', array_keys($prepared)) . ')' . " VALUES " . '(' . implode(', ', array_values($prepared)) . ')';
} else {
//Monta a string de UPDATE
$sql = "UPDATE {$this->getEntity()}";
//Monta os pares: coluna=valor,...
if ($prepared) {
foreach($prepared as $column => $value) {
if ($column !== 'id') {
$set[] = "{$column} = {$value}";
}
}
}
$sql .= " SET " . implode(', ', $set);
$sql .= 'WHERE id = ' . (int) $this->date['id'];
}
//Obtém transação ativa
if ($conn = Transaction::get()) {
Transaction::log($sql);
$result = $conn->exec($sql);
return $result;
} else {
throw new Exception('Não há transação ativa');
}
}
public function load($id)
{
//Monta instrução de SELECT
$sql = "SELECT * FROM {$this->getEntity()}";
$sql .= ' WHERE id = ' . (int) $id;
//Obtém transação ativa
if ($conn = Transaction::get()) {
//Cria a mensagem de log e executa a consulta
Transaction::log($sql);
$result = $conn->query($sql);
//Se retornou algum dado
if ($result) {
//Retorna os dados em forma de objeto
$object = $result->fetchObject(get_class($this));
}
return $object;
} else {
throw new Exception('Não há transação ativa!!');
}
}
public function delete($id = NULL)
{
//O ID é o parâmetro ou a propriedade ID
$id = $id ? $id : $this->id;
//Mostra a string de UPDATE
$sql = "DELETE FROM {$this->getEntity()}";
$sql .= ' WHERE id = ' . (int) $this->data['id'];
//Obtém transação ativa
if ($conn = Transaction::get()) {
//Faz o log e executa o SQL
Transaction::log($sql);
$result = $conn->exec($sql);
return $result; //Retorna o resultado
} else {
throw new Exception('Não há transação ativa!!');
}
}
public static function find($id)
{
$classname = get_called_class();
$ar = new $classname;
return $ar->load($id);
}
private function getLast()
{
if($conn = Transaction::get()) {
$sql = "SELECT max(id) FROM {$this->getEntity()}";
//Cria log e executa instrução SQL
Transaction::log($sql);
$result = $conn->query($sql);
//Retorna os dados do banco
$row = $result->fetch();
return $row[0];
} else {
throw new Exception('Não há transação ativa!!');
}
}
public function prepare($data)
{
$prepared = array();
foreach ($data as $key => $value) {
if(is_scalar($value)) {
$prepared[$key] = $this->escape($value);
}
}
return $prepared;
}
public function escape($value)
{
if (is_string($value) and (!empty($value))) {
//Adiciona \ em aspas
$value = addslashes($value);
return "'$value'";
} else if (is_bool($value)) {
return $value ? 'TRUE' : 'FALSE';
} else if ($value !== '') {
return $value;
} else {
return "NULL";
}
}
}
?>
Product class
<?php
class Produto extends Record {
const TABLENAME = 'produto';
}
?>
Expression class
<?php
abstract class Expression
{
//Operadores lógicos
const AND_OPERATOR = 'AND ';
const OR_OPERATOR = 'OR ';
//Metodo que retornara uma expressão em string
abstract public function dump();
}
?>
Class Criteria
<?php
class Criteria extends Expression
{
private $expressions; //Armazena a lista de expressões
private $operators; //Armazena a lista de operadores
private $properties; //Propriedades do critério (ORDER BY, LIMIT, ...)
public function __construct()
{
$this->expressions = array();
$this->operators = array();
}
public function add(Expression $expression, $operator = self::AND_OPERATOR)
{
//Na primeira vez, não precisamos concatenar
if (empty($this->expressions)) {
$operator = NULL;
}
//Agrega o resultado da expressão para a lista de expressões
$this->expressions[] = $expression;
$this->operators[] = $operator;
}
public function dump()
{
//Concatena a lista de expressões
if (is_array($this->expressions)) {
if (count($this->expressions) > 0) {
$result = '';
foreach ($this->expressions as $i => $expression) {
$operator = $this->operators[$i];
//Concatena o operador com a respectiva expressão
$result .= $operator . $expression->dump() . ' ';
}
$result = trim($result);
return "({$result})";
}
}
}
public function setProperty($property, $value)
{
if (isset($value)) {
$this->properties[$property] = $value;
} else {
$this->properties[$property] = NULL;
}
}
public function getProperty($property)
{
if (isset($this->properties[$property])) {
return $this->properties[$property];
}
}
}
?>
Filter class
<?php
class Filter extends Expression
{
public $variable;
public $operator;
public $value;
public function __construct($variable, $operator, $value)
{
$this->variable = $variable;
$this->operator = $operator;
//Transforma o valor de acordo com certas regras de tipo
$this->value = $this->transform($value);
}
private function transform($value)
{
//Caso seja um array
if (is_array($value)) {
foreach ($value as $x) {
if (is_integer($x)) {
$foo[] = $x;
} else if (is_string($x)) {
$foo[] = "'$x'";
}
}
//Converte o array em string separada por ","
$result = '(' . implode(',', $foo) . ')';
} else if (is_string($value)) {
$result = "'$value'";
} else if (is_null($value)) {
$result = 'NULL';
} else if (is_bool($value)) {
$result = $value ? 'TRUE' : 'FALSE';
} else {
$result = $value;
}
//Retorna o valor
return $result;
}
public function dump()
{
//Concatena a expressão
return "{$this->variable} {$this->operator} {$this->value}";
}
}
?>
Repository class
<?php
class Repository
{
private $activeRecord;
public function __construct($class)
{
$this->activeRecord = $class;
}
public function load(Criteria $criteria = NULL)
{
$sql = "SELECT * FROM " . constant($this->activeRecord.'::TABLENAME');
if ($criteria) {
$expression = $criteria->dump();
if ($expression) {
$sql .= ' WHERE ' . $expression;
}
$order = $criteria->getProperty('ORDER');
$limit = $criteria->getProperty('LIMIT');
$offset = $criteria->getProperty('OFFSET');
if ($order) {
$sql .= ' ORDER ' . $order;
}
if ($limit) {
$sql .= ' LIMIT ' . $limit;
}
if ($offset) {
$sql .= ' OFFSET ' . $offset;
}
}
if ($conn = Transaction::get()) {
Transaction::log($sql);
$result = $conn->query($sql);
$results = array();
if ($result) {
while ($row = $result->fetchObject($this->activeRecord)) {
$results[] = $row;
}
}
return $results;
} else {
throw new Exception('Não há transição ativa!!');
}
}
public function delete(Criteria $criteria = NULL)
{
$sql = "DELETE FROM " . constant($this->activeRecord.'::TABLENAME');
if ($criteria) {
$expression = $criteria->dump();
if ($expression) {
$sql .= ' WHERE ' . $expression;
}
}
if ($conn = Transaction::get()) {
Transaction::log($sql);
$result = $conn->exec($sql);
return $result;
} else {
throw new Exception('Não há transição ativa!!');
}
}
public function count(Criteria $criteria = NULL)
{
$sql = "SELECT count(*) FROM " . constant($this->activeRecord.'::TABLENAME');
if ($criteria) {
$expression = $criteria->dump();
if ($expression) {
$sql .= ' WHERE ' . $expression;
}
}
if ($conn = Transaction::get()) {
Transaction::log($sql);
$result = $conn->query($sql);
if ($result) {
$row = $result->fetch();
}
return $row[0];
} else {
throw new Exception('Não há transição ativa!!!');
}
}
}
?>
Finally, the example of use
<?php
require_once 'classes/api/Transaction.php';
require_once 'classes/api/Connection.php';
require_once 'classes/api/Expression.php';
require_once 'classes/api/Criteria.php';
require_once 'classes/api/Repository.php';
require_once 'classes/api/Record.php';
require_once 'classes/api/Filter.php';
require_once 'classes/api/Logger.php';
require_once 'classes/api/LoggerTXT.php';
require_once 'classes/model/Produto.php';
try {
//Inicia a transação com a base de dados
Transaction::open('estoque');
//Define o arquivo de LOG
Transaction::setLogger(new LoggerTXT('tmp/log_collection_update.txt'));
//Define o criterio de seleção
$criteria = new Criteria;
$criteria->add(new Filter('preco_venda', '<=', 35));
$criteria->add(new Filter('origem', '=', 'N'));
//Cria o repositório
$repository = new Repository('Produto');
//Carrega os objetos conforme o critério
$produtos = $repository->load($criteria);
if ($produtos) {
//Percorre todos os objetos
foreach ($produtos as $produto) {
$produto->preco_venda *= 1.3;
$produto->store(); //Método da classe Record
}
}
Transaction::close();
} catch (Exception $e) {
Transaction::rollback();
print $e->getMessage();
}
Improve your question by putting the definitions that the book brings and, if there are examples of codes, put them as well. Don’t forget to reference the book and author.
– Woss