Ir para conteúdo

Arquivado

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

TNT

Log em PHP/MySql

Recommended Posts

Olá a todos.

 

Recebi um pedido que fiquei em dúvida quanto à viabilidade do projeto. Gostaria de uma opinião de vocês sobre o assunto.

 

O cliente me solicitou o desenvolvimento de um sistema de log, que armazenaria todas as operações efetuadas por um conjunto de usuários. Ou seja, no momento que um administrador do site cadastrasse, deletasse ou mesmo modificasse os dados de algum usuário, o sistema registraria a operação no log.

 

Cadastrar e deletar entendo como mais trivial. O problema é quanto à modificação. Existe algum meio prático de armazenar quais dos campos de uma tabela tiveram seus dados modificados? Levando para um exemplo prático: em um formulário que contenha nome e CPF de uma pessoa, se o administrador modifica apenas o CPF, teria como eu armazenar esta informação no log, com uma mensagem no estilo "O administrador modificou o CPF"?

 

Obrigado e até mais.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Amigo TNT o meio mais pratico para você resolver isso seria ao invés de armazenar campos,

armazenar a query no LOG ex. campos da tabela log: usuario, data, acao, query

Dai você vai poder armazenar cada passo do usuário e saber de quebra qual query foi executada.

 

Assim quando precisar saber quem fez x alteração basta procurar por ela no log. ;)

 

Abraços.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Boa, não havia pensado nisso e gostei bastante da idéia. Só existe um problema, que seria identificar qual o campo foi modificado dentre os existentes na query. No meu exemplo, em um formulário que contém nome e CPF, armazenando a query inteira eu não saberia qual dos dois campos foi modificado não?

 

Ou eu que não entendi bem?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Antes de executar a query que modifica, você recebe o dado atual do banco e guarda numa variável. Depois, executa a query de atualização do dado e cria uma segunda variável que tem os novos dados. Use uma função que compare as duas variáveis geradas, gerando uma só variável e depois salve no banco.

 

Acho que a função strcmp() pode ajudar.

B)

Compartilhar este post


Link para o post
Compartilhar em outros sites

Talvez eu não tenha entendido bem. Mas pelo que sei você vai precisar saber qual campo

foi alterado caso alguém lhe pergunte não?

 

Ex: Chefe: Quem foi o f.... que mudou o nome do cliente?

Você: Quando foi a ultima vez que o nome estava correto e qual era o nome?

Chefe: Até ontem estava Lucas Silva Castro e hoje fui ver e esta aparecendo Lucas S.C.

Você: Um minuto chefe.

 

Abre o arquivo de log de ontem e hoje e procura queries que contenham Lucas S.C

ao encontrar verifica o usuário que fez a alteração e reporta ao chefe.

 

Não seria essa a utilidade???

 

Abraços.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Nesse caso, além de comparar as duas querys, ele também vai pegar algumas outras coisas, como data, hora, IP da máquina e um cookie ou session, sei lá, com o nome do funcionário. Aí o chefe não vai ter dúvidas de "quem foi o f.... que mudou o nome do cliente".

 

veja a ordem que proponho:

  1. Receber a query atual e guardar em variável
  2. Pegar a query que faz a modificação e guardar
  3. pegar data, hora, IP, cookie, session...
  4. comparar as duas primeiras variáveis e deixar apenas a modificação
  5. juntar tudo em uma variável, ou array (serialize)
  6. salvar tudo em uma tabela.

 

Abraços.

:skull: :ninja:

Compartilhar este post


Link para o post
Compartilhar em outros sites

Certo, a idéia é esta mesma, mas com um detalhe: o próprio "chefe" quer ter acesso às mensagens de log, indicando qual o campo modificado. E para ele, que é leigo em programação, não seria "amigável" que a mensagem simplesmente fosse a query, entendem? Então eu gostaria que fosse uma mensagem no estilo "f.... modificou o nome do associado de 'Lucas Silva Castro' para 'Lucas S.C.'". Mas para isto eu precisaria descobrir qual campo exatamente foi modificado.

 

A função strcmp acho que não se aplicaria neste caso, já que retorna apenas a igualdade ou não de duas strings.. O que eu precisaria mesmo é de alguma maneira que me fizesse descobrir qual o campo modificado e, até agora, não consigo pensar em outra solução que não seja comparando campo a campo. Problema é que eu acho isto pouco eficiente..

Compartilhar este post


Link para o post
Compartilhar em outros sites

Na verdade não tem segredo amigo TNT.

 

Ele quer saber quem modificou X campo não?

Basta você retornar para ele a primeira ocorrencia da tabela logs

onde o nome foi alterado para Lucas S. C. fazendo a pesquisa pela query.

 

Se ele quiser saber quem mudou o endereço faça a pesquisa pelo endereço novo.

A primeira ocorrencia de update com o endereço novo já traz todos os dados de quem fez.

 

Pegou a idéia?? ;)

 

Fica bem mais fácil se o log for uma tabela no banco de dados.

Mas se desejar fazer em arquivo texto talvez o link abaixo lhe ajude

na hora de pesquisar por um valor alterado.

 

http://phpbrasil.com/phorum/read.php?1,21488

 

Abraços.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Até entendi o que você quer dizer, acho, mas para o que eu pretendo creio que não funcione..

 

Veja bem, no formato que você está sugerindo, teria que ter um campo para que ele pesquisasse nos logs pela modificação do nome Lucas S.C., certo? Mas o que eu preciso não é exatamente isto, e sim fazer uma página com o formato parecido com isto:

 

- 14h30min: "Admin 1" modificou o nome de "Usuario 1" para "Usuaaaario 1".

- 14h35min: "Admin 2" modificou o CPF de "Usuario 1" para "111.111.111-11".

 

 

Ou seja, uma página com todas as movimentações do sistema e não um sistema de busca que gere resultados. Agora pense na seguinte query que dou como exemplo:

 

"UPDATE usuarios SET nome = '$nome', cpf = '$cpf' WHERE id = '$id'"

 

Se eu salvar uma query nesse estilo, não tenho como gerar uma página com a definição que pretendo, concorda? Até por que simplesmente executando esta query não saberei exatamente quais os campos estarão sendo modificados. A única alternativa que vejo é passar o que possuo previamente no banco para variáveis e comparar com o que estou inserindo.

 

Não sei se consegui me expressar bem, nem se estou demorando a entender uma idéia óbvia, mas enfim.. obrigado pela ajuda.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Você pode utilizar Command para fazer isso:

 

gallery_94216_34_3438.png

 

Command: Padrão de comportamento

Participantes:

Command
:

Declara uma interface para executar uma operação.

ConcreteCommand
:

Cria um vinculo entre o Receiver e uma ação e implementa a interface do Command para chamar a ação apropriada no Receiver.

Client
:

Cria o ConcreteCommand e define seu Receiver.

Invoker
:

Invoca a ação do Command.

Receiver
:

Sabe como executar a ação, qualquer um que souber como executar determinada ação pode atuar como Receiver.

<?php
class User {
private $id;
private $name;

public function __construct( $id = null , $name = null ) {
	$this->setId( $id );
	$this->setName( $name );
}

public function delete() {
	echo 'Apagando usuário' , PHP_EOL;
	//DELETE FROM `User` WHERE `id`=:id;
}

public function insert() {
	echo 'Criando usuário' , PHP_EOL;
	//INSERT INTO `User`(`name`) VALUES(:name);
}

public function update() {
	echo 'Atualizando usuário' , PHP_EOL;
	//UPDATE `User` SET `name`=:name WHERE `id`=:id;
}

public function getId() {
	return $this->id;
}

public function getName() {
	return $this->name;
}

public function setId( $id ) {
	$this->id = $id;
}

public function setName( $name ) {
	$this->name = $name;
}
}

 

A classe User será nossa entidade, mas também será nosso Receiver.

 

<?php
interface Command {
public function execute();
}

 

<?php
abstract class AbstractCommand implements Command {
protected $user;

final public function __construct( User $user ) {
	$this->user = $user;
}
}

 

AbstractCommand é apenas para reutilizar código, a implementação do método execute() é do ConcreteCommand:

 

<?php
class CreateCommand extends AbstractCommand {
public function execute() {
	$this->user->insert();
}
}

 

<?php
class UpdateCommand extends AbstractCommand {
public function execute() {
	$this->user->update();
}
}

 

<?php]
class DeleteCommand extends AbstractCommand {
public function execute() {
	$this->user->delete();
}
}

 

<?php
class Logger {
public function log( Command $command ) {
	printf( "Gravando log para o comando %s\n" , get_class( $command ) );
	//INSERT INTO `Log` ...
}
}

 

Um Invoker "besta":

<?php
class UserInvoker {
private $logger;

public function __construct() {
	$this->logger = new Logger();
}

public function create( Command $createCommand ) {
	$this->doLog( $createCommand );
	$createCommand->execute();
}

public function update( Command $updateCommand ) {
	$this->doLog( $updateCommand );
	$updateCommand->execute();
}

public function delete( Command $deleteCommand ) {
	$this->doLog( $deleteCommand );
	$deleteCommand->execute();
}

private function doLog( Command $command ) {
	$this->logger->log( $command );
}
}

 

E um Client "mais besta" ainda:

<?php
class UserModel {
public function test() {
	$user = new User( 1 , 'Neto' );
	$invoker = new UserInvoker();

	$invoker->create( new CreateCommand( $user ) );

	$user->setName( 'João Batista Neto' );

	$invoker->update( new UpdateCommand( $user ) );
	$invoker->delete( new DeleteCommand( $user ) );
}
}

 

 

Usando isso ai:

<?php
$userModel = new UserModel();
$userModel->test();

 

Saída:

 

Gravando log para o comando CreateCommand

Gravando log para o comando UpdateCommand

Gravando log para o comando DeleteCommand

 

Ok, mas obviamente seu cliente não quer apenas ver o que está acontecendo, ele quer que, caso alguma coisa errada tenha acontecido, ele possa reverter.

 

E ai ?

 

Uma das consequências de Command é a possibilidade de você poder proporcionar um "rollBack" ou seja, oferecer um "Ctrl + z" para seu cliente.

 

Mas, para oferecer o Ctrl + z para seu cliente, você precisará guardar o estado atual do objeto, antes de executar a ação, para isso usamos outro padrão:

 

gallery_94216_34_5445.png

 

Atualizando nossos participantes:

 

<?php
/**
* Interface para um comando que pode ser executado mas
* que pode ser revertido
*/
interface Command {
/**
 * Executa o comando
 */
public function execute();

/**
 * Reverte o comando
 */
public function rollBack();
}

 

<?php
/**
* Caretaker é o cara que cuida dos nossos logs e mantém um
* histórico de tudo que aconteceu.
*/
class Caretaker {
private $history = array();

public function store( Command $command , Memento $memento ) {
	array_unshift( $this->history , array( $command , $memento ) );

	printf( "Gravando log para o comando %s\n" , get_class( $command ) );
	//INSERT INTO `Log` ...
}

public function recover( $level = 1 ) {
	for ( $i = 0 ; isset( $this->history[ $i ] ) && $i < $level ; ++$i ) {
		$command = $this->history[ $i ][ 0 ];

		printf( "Revertendo comando %s\n" , get_class( $command ) );

		$command->setMemento( $this->history[ $i ][ 1 ] );
		$command->rollBack();
	}
}
}

 

<?php
/**
* A entidade usuário, ela precisa ser serializável porque
* precisaremos guardá-la no banco de dados, caso seja
* necessário revertê-la a um estado anterior
*/
class User implements Serializable {
private $id;
private $name;

public function __construct( $id = null , $name = null ) {
	$this->setId( $id );
	$this->setName( $name );
}

public function delete() {
	//DELETE FROM ...
}

public function insert() {
	//INSERT INTO ...
}

public function update() {
	//UPDATE ...
}

public function getId() {
	return $this->id;
}

public function getName() {
	return $this->name;
}

public function setId( $id ) {
	$this->id = $id;
}

public function setName( $name ) {
	$this->name = $name;
}

public function serialize() {
	return serialize( array( 'id' => $this->id , 'name' => $this->name ) );
}

public function unserialize( $serialized ) {
	$data = unserialize( $serialized );

	if ( isset( $data[ 'id' ] ) && isset( $data[ 'name' ] ) ) {
		$this->id = (int) $data[ 'id' ];
		$this->name = $data[ 'name' ];
	} else {
		throw new UnexpectedValueException( 'Erro ao deserializar objeto' );
	}
}
}

 

<?php
/**
* O Memento manterá uma cópia de estado antes de alguma coisa
* ser feita
*/
class Memento {
private $state;

public function __construct( $state ) {
	$this->setState( $state );
}

public function setState( $state ) {
	$this->state = $state;
}

public function getState() {
	return $this->state;
}
}

 

<?php
/**
* O Originator é também a base para nossos comandos,
* com ele poderemos executar ações, mas também poderemos
* desfazer "khdas"
*/
abstract class Originator implements Command {
protected $user;

final public function __construct( User $user ) {
	$this->user = $user;
}

public function createMemento() {
	return new Memento( $this->user );
}

public function setMemento( Memento $memento ) {
	$this->user = $memento->getState();
}
}

 

<?php
/**
* Grava uma entidade no banco, mas também apaga caso necessário
*/
class CreateCommand extends Originator {
public function execute() {
	$this->user->insert();
}

public function rollBack() {
	$this->user->delete();
}
}

 

<?php
/**
* Atualiza uma entidade no banco, mas também reverte a atualização
*/
class UpdateCommand extends Originator {
public function execute() {
	$this->user->update();
}

public function rollBack() {
	$this->user->update();
}
}

 

<?php
/**
* Apaga uma entidade do banco, mas também grava novamente se necessário
*/
class DeleteCommand extends Originator {
public function execute() {
	$this->user->delete();
}

public function rollBack() {
	$this->user->insert();
}
}

 

<?php
/**
* O cara que será acionado por nosso client para efetuar as ações
*/
class UserInvoker {
private $caretaker;

public function __construct() {
	$this->caretaker = new Caretaker();
}

public function execute( Command $createCommand ) {
	$this->register( $createCommand );
	$createCommand->execute();
}

public function rollBack( $level = 1 ) {
	$this->caretaker->recover( $level );
}

private function register( Command $command ) {
	$this->caretaker->store( $command , $command->createMemento() );
}
}

 

<?php
/**
* Nosso Client
*/
class UserModel {
private $invoker;

public function __construct() {
	$this->invoker = new UserInvoker();
}

public function test() {
	$user = new User( 1 , 'Neto' );

	$this->invoker->execute( new CreateCommand( $user ) );

	$user->setName( 'João Batista Neto' );

	$this->invoker->execute( new UpdateCommand( $user ) );
	$this->invoker->execute( new DeleteCommand( $user ) );
}

public function opz( $level = 1 ) {
	$this->invoker->rollBack( $level );
}
}

 

Usando:

 

<?php
$userModel = new UserModel();
$userModel->test();
$userModel->opz( 1 ); //Revertendo apenas o comando Delete

 

Saída:

 

Gravando log para o comando CreateCommand

Gravando log para o comando UpdateCommand

Gravando log para o comando DeleteCommand

Revertendo comando DeleteCommand

 

;)

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.