PHP::PDO Básico – O que se precisa saber para criar um CRUD

php_logo2

PHP::PDO Básico do básico – O que se precisa saber para criar um CRUD


Resolvi escrever este artigo breve porque hoje (21/06/2019) estava acompanhando uma rede social e um dos participantes estava com uma dúvida básica sobre PDO.

Talvez não seja o caso para ele, mas hoje muitos estão entrando no mundo da programação web e PHP é uma linguagem muito escolhida por muitos.

PHP ainda é uma da liguagens mais utilizadas no mundo no lado Backend para dar vida a sistemas Web. Se ela não for a mais utilizada ainda!

Por isso, saber ir além do Hello World e criar sistemas é o que muitos novos tem almejado e tem se esforçado. PDO define uma interface leve e consistente para acesso a banco de dados.

PDO não substitui a construção de queries, contudo, entrega uma camada de abstração de acesso a dados que permite na maioria das vezes, a criação de queries funcionais aplicadas em sistemas de bancos de dados diferentes.

Chega de enrolação, então o que é necessário se saber para utilizar PDO e criar CRUD?

Primeiro a Conexão

PDO é uma classe que precisa ser instanciada. É feito desta maneira:

Por exemplo para MySQL:

	
		$pdo = new PDO("mysql:host=meu_servidor; dbname=meubanco", "meu_usuario", "minha_senha");
	
	

O que aparece entre as primeiras apas é conhecido muitas vezes como DSN (Data Source Name), mas o correto é entender como uma string de conexão que especifica informações sobre uma fonte de dados. Veja o que a Wikipedia diz:

… É passado o código para um controlador (driver) ou provedor subjacente com o objetivo de se iniciar a conexão. Apesar de comumente ser usado para conexão de banco de dados, a fonte de dados também pode ser uma planilha eletrônica ou um arquivo de texto.

A cadeia de caracteres de conexão pode incluir atributos como o nome do controlador (driver), servidor e banco de dados, bem como informações de segurança como nome de usuário e senha. (Jota naici).

Acesso feito em 21/06/2019

Dessa maneira, tomando como exemplo um servidor de bancos de dados MySQL com ip 192.168.0.10, banco de dados erp_database usuário db_admin e senha Abcd1234, a conexão seria:

	
		$pdo = new PDO("mysql:host=192.168.0.10; dbname=erp_database", "db_admin", "Abcd1234");
	
	

Lembrando que havendo alguma semelhança com alguma configuração do banco de dados de alguém, será mera concidência.

Em um ambiente de desenvolvimento, é comum a utilização do usuário root, mas em um ambiente real, muitas vezes haverá um usuário com permissões de operação sobre determinado banco de dados.

Para conectar ao banco de dados basta uma linha como esta no PHP. Só isto mesmo.

Mas é comum ser feito algumas definições tal como o meio como a classe deverá responder em caso de erro, o charset etc.

Para fazer estas definições, se utiliza algo desta maneira:

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

Notar que o atributo fora denifido na instância do objeto PDO. Pode-se até executar algo como que queira que seja feito quando a conexão for estabelecida.

	
		$pdo->exec("SET NAMES utf8");
	
	

Mas tudo isso também poderia ser feito desde o momento em que se gera a instância do PDO:

	
		$pdo = new PDO("mysql:host=192.168.0.10; dbname=erp_database", "db_admin", "Abcd1234", array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));
	
	

Faça da maneira como preferir!

É recomendável então colocar esta declaração em um bloco try e catch para que seja possivel tratar alguma excessão:

	
		try {
		
			$pdo = new PDO("mysql:host=192.168.0.10; dbname=erp_database", "db_admin", "Abcd1234", array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));
			
		} catch (PDOException $e) {
			echo 'Erro: ' . $e->getMessage();
		}
	
	

Listando dados

Tendo o objeto de conexão, pode-se fazer a listagem:

	
		try {

			$stmt = $pdo->prepare("SELECT * FROM tb_clientes");

			if ($stmt->execute()) {
				while ($rs = $stmt->fetch(PDO::FETCH_OBJ)) {
					
					var_dump($rs); // O resultado neste caso gera um objeto por linha
					
				}
			} else {
				echo "Erro: Não foi possível recuperar os dados do banco de dados";
			}
		} catch (PDOException $erro) {
		
			echo "Erro: ".$erro->getMessage();
		}
	
	

O método prepare vai devolver um objeto PDOStatement que poderá se executado a qualquer momento. No exemplo acima, já é suficiente para gerar um resultado, mas se for o caso de se passar parâmetros para uma filtragem? Seria possivel passar alí diretamente na declaração de query, mas o ideal é se valer da segurança proposta pelo objeto PDOStatment. Por exemplo, para buscar clientes com um determinado id:

	
		try {

			$stmt = $pdo->prepare("SELECT * FROM tb_clientes WHERE id = :id");
			$stmt->bindValue(':id', $id, PDO::PARAM_INT);

			if ($stmt->execute()) {
				while ($rs = $stmt->fetch(PDO::FETCH_OBJ)) {
					
					var_dump($rs); // O resultado neste caso gera um objeto por linha
					
				}
			} else {
				echo "Erro: Não foi possível recuperar os dados do banco de dados";
			}
		} catch (PDOException $erro) {
		
			echo "Erro: ".$erro->getMessage();
		}
	
	

Notar que o código id vem de uma variável $id, e no caso é tratado como um valor inteiro. Bind faz exatamente o que significa em inglês: ligar ou vincular

BindValue vincula o valor da variável id ao “parâmetro nomeado” :id. Além disso, já que se espera um valor do tipo inteiro, informando PDO::PARAM_INT, não haverá risco de “SQL Injection”. Neste caso foi inteiro, mas se o que se pretende informar for string:

	
		try {

			$stmt = $pdo->prepare("SELECT * FROM tb_clientes WHERE primeiro_nome LIKE :p_nome OR sobre_nome LIKE :s_nome");
			$stmt->bindValue(':p_nome', '%'. $p_nome. '%', PDO::PARAM_STR);
			$stmt->bindValue(':S_nome', '%'. $s_nome. '%', PDO::PARAM_STR);

			if ($stmt->execute()) {
				while ($rs = $stmt->fetch(PDO::FETCH_OBJ)) {
					
					var_dump($rs); // O resultado neste caso gera um objeto por linha
					
				}
			} else {
				echo "Erro: Não foi possível recuperar os dados do banco de dados";
			}
		} catch (PDOException $erro) {
		
			echo "Erro: ".$erro->getMessage();
		}
	
	

Notar que dois valores são recebidos em variáveis $p_nome para primeiro nome e $s_nome para sobrenome. Portanto, espera-se que sejam strings. O objeto PDOStatment vai tratar como string o que vier nestas variáveis.

Se por algum motivo for necessário tratar se bindValue vinculou corretamente, um valor booleano será retornado:

	
		var_dump($stmt->bindValue(':p_nome', '%'. $p_nome. '%', PDO::PARAM_STR)); // Retornará true ou false
	
	

Uma vez vinculado os valores, utiliza-se o método execute para naturalmente, executar a querie. Este método irá retornar um valor booleano true ou false. Desta maneira já há uma forma forma de tratar erros.

		
		if ($stmt->execute()) {
            while ($rs = $stmt->fetch(PDO::FETCH_OBJ)) {
                
				var_dump($rs); // O resultado neste caso gera um objeto por linha
				
            }
        } else {
            echo "Erro: Não foi possível recuperar os dados do banco de dados";
        }
		
	

Notar que no if, se o teste resultar false, o else será invocado e apresentará a mensagem de erro.

Uma vez tendo resultado positivo em execute(), pode-se buscar os resultados utilizando um método apropriado. Neste caso como espera-se iterar linha a linha, utiliza-se o fetch(). O fetch retorna uma linha a cada iteração e é possivel determinar como se deseja este retorno. No exemplo esperamos que cada row, seja um objeto, tendo cada coluna como um atributo:

		
		while ($rs = $stmt->fetch(PDO::FETCH_OBJ)) {
               
			var_dump($rs); // O resultado neste caso gera um objeto por linha
				
        }
		
	

Mas se o desejo for array associativo:

		
		while ($rs = $stmt->fetch(PDO::FETCH_ASSOC)) {
               
			var_dump($rs); // O resultado neste caso gera um array associativo por linha
				
        }
		
	

Ainda há outras opções e argumentos possíveis para fetch, mas talvez não seja apropriado abranger todos aqui, então caso desejar saber mais, talvez queira consultar a documentação: https://www.php.net/manual/pt_BR/pdostatement.fetch.php

Além disso, a necessidade talvez seja obter o retorno do resultado como um todo para ser tratado via código de outra maneira. então pode-se ter um “result set” utilizando o fetchAll():

		
		if ($stmt->execute()) {
                
			var_dump($stmt->fetchAll(); // Retorna um array multdimensional com arrays representando rows
	        
        } else {
            echo "Erro: Não foi possível recuperar os dados do banco de dados";
        }
		
	

Mas fetchAll() é muito mais do que isso. Caso o projeto contar com uma classe que deva representar o resultado estando preparada para receber os atributos, será perfeitamente possível gerar o vinculo e retornar um array como um repositório de resultados:

			
		if ($stmt->execute()) {
                
			var_dump($stmt->fetchAll(PDO::FETCH_CLASS, 'Cliente'); // Retorna um array de objetos 'Cliente' com resultados vinculados a suas propriedades 
	        
        } else {
            echo "Erro: Não foi possível recuperar os dados do banco de dados";
        }
		
	

Se o que se espera é obter um array de objetos e não necessáriamente especificados por uma Classe, apenas deve-se deixar de informar o segundo parâmtro:

			
		if ($stmt->execute()) {
                
			var_dump($stmt->fetchAll(PDO::FETCH_CLASS); // Retorna um array de objetos com resultados vinculados a suas propriedades 
	        
        } else {
            echo "Erro: Não foi possível recuperar os dados do banco de dados";
        }
		
	

Novamente a documentação apresenta outras configurações possiveis para fetchAll: https://www.php.net/manual/pt_BR/pdostatement.fetchall.php

Talvez o que se espera é um único resultado, uma unica linha, então o fetch irá atender bem:

	
		try {

			$stmt = $pdo->prepare("SELECT * FROM tb_clientes WHERE id = :id");
			$stmt->bindValue(':id', $id, PDO::PARAM_INT);
			
			if ($stmt->execute()) {
				
				$cliente = $stmt->fetch(PDO::FETCH_OBJ); // O resultado neste caso gera um objeto
				$id = $cliente->id;
				$p_nome = $cliente->primeiro_nome;
				$s_nome = $cliente->sobre_nome;
				$email = $cliente->email;
				$celular = $cliente->celular;
					
					
			} else {
				echo "Erro: Não foi possível recuperar os dados do banco de dados";
			}
		
		} catch (PDOException $erro) {
		
			echo "Erro: ".$erro->getMessage();
		}	
	
	

Embora o código acima já tenha sido apresentado, mas agora foi detalhado em relação a extração de dados do retorno para que se possa compreender o seu uso. O que será importante destacar aqui é a possibilidade de tratar o fetch de formas diferentes:

		
		if ($stmt->execute()) {
            
			$cliente = $stmt->fetch(PDO::FETCH_ASSOC); // O resultado neste caso gera um objeto
			$id = $cliente['id'];
            $p_nome = $cliente['primeiro_nome'];
			$s_nome = $cliente['sobre_nome'];
            $email = $cliente['email'];
            $celular = $cliente['celular'];
				
				
        } else {
            echo "Erro: Não foi possível recuperar os dados do banco de dados";
        }
	
	

Mas pode ser que, a intenção seja criar um objeto DAO ou Active Record que faça o vinculo do resultado na própria classe. Então, seria perfeitamente possível com o uso do fetchObject:

		
		if ($stmt->execute()) {
            
			$cliente = $stmt->fetchObject(__CLASS__); // O resultado neste caso gera um objeto
						
				
        } else {
            echo "Erro: Não foi possível recuperar os dados do banco de dados";
        }
	
	

Para dar certo, a classe deverá trabalhar com métodos mágicos __set() e __get(). Logo a seguir, há um exemplo de uma classe, bem simplificada que apresenta um idéia inicial da construção de classes deste tipo:


		/**
		 * Classe Cliente
		 *
		 */
		class Cliente
		{
			
			protected $pdo;
			
			protected $atributos;
			
			
			public function __construct($host, $database, $user, $pass)
			{
				try {
		
					$this->pdo = new PDO("mysql:host={$host}; dbname={$database}", $user, $pass,
					array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));
			
				} catch (PDOException $e) {
					echo 'Erro: ' . $e->getMessage();
				}
			
			}
			
			public function __set($atributo, $valor)
			{
				$this->atributos[$atributo] = $valor;
			}
			
			public function __get($atributo)
			{
				return $this->atributos[$atributo];
			}
			
			public function find(int $id) 
			{
				try {

					$stmt = $this->pdo->prepare("SELECT * FROM tb_clientes WHERE id = :id");
					$stmt->bindValue(':id', $id, PDO::PARAM_INT);
			
					if ($stmt->execute()) {
				
						return $stmt->fetchObject(__CLASS__); // O resultado neste caso gera um objeto
						
					} else {
					
						echo "Erro: Não foi possível recuperar os dados do banco de dados";
					}
			
				} catch (PDOException $erro) {
		
					echo "Erro: ".$erro->getMessage();
				}	
			
			}

		}
		
		// Exemplo de uso
		$clienteDao = new Cliente('192.168.0.10', 'erp_database', 'db_admin', 'Abcd1234');
		$cliente = $clienteDao->find(1);
		
		echo $cliente->primeiro_nome . ' ' $cliente->sobre_nome; 
		
	
	

Mas este foi apenas um exemplo bem simplificado. Se desejar, poderá consultar alguns artigos neste blog com exemplos completos, totalmente funcionais

Inserindo, atualizando e excluíndo dados

Parte do código aqui já será familiar. Indo direto ao ponto, pode-se fazer o vinculo dos dados da mesma maneira na declaração:

	
		$stmt = $pdo->prepare("INSERT INTO tb_clientes (primeiro_nome, sobre_nome, email, celular) VALUES (:p_nome, :s_nome, :email, :celular)");
		
		$stmt->bindValue('p_nome', $primeiro_nome, PDO::PARAM_STR);
		$stmt->bindValue('s_nome', $sobre_nome, PDO::PARAM_STR);
		$stmt->bindValue('email', $email, PDO::PARAM_STR);
		$stmt->bindValue('celular', $celular, PDO::PARAM_STR);
		
		if ($stmt->execute()) {
			if ($stmt->rowCount() > 0) {
				echo "Cadastro efetuado com sucesso!";
			} else {
				echo "Não foi possível fazer o cadastro.";
			}
		} else {
			throw new PDOException("Erro: Não foi possível executar a declaração SQL");
		}

	

Notar que foram omitidos as declarações de blocos try e catch, mas que devem ser incluídos para que o lançamento da exceção possa ser feito normalmente em caso de falha.

Ainda, também supõe-se que o leitor entenda até aqui que as variáveis precisam ser passadas com seus devidos valores. Estes detalhes foram omitidos para manter os exemplos concisos.

É possivel também passar todos estes dados de uma só vez no momento da execução. O problema é não poder definir quais tipos de dados estão sendo passados, como faríamos através dos métodos bindValue() e bindParam(). Mas para que possa ser conhecido, segue-se. Primeiro cria-se o array e depois informa com argumento no execute:

	
		$valores = [
			'primeiro_nome' => 'João',
			'sobre_nome' => 'Silva',
			'email' => 'jsilva@teste.com.br',
			'celular' => '9999-0000'
		];
		
		$stmt = $pdo->prepare("INSERT INTO tb_clientes (primeiro_nome, sobre_nome, email, celular) VALUES (:p_nome, :s_nome, :email, :celular)");
		
		if ($stmt->execute($valores)) {
			if ($stmt->rowCount() > 0) {
				echo "Cadastro efetuado com sucesso!";
			} else {
				echo "Não foi possível fazer o cadastro.";
			}
		} else {
			throw new PDOException("Erro: Não foi possível executar a declaração SQL");
		}
	
	

Se fosse uma atualização, o processo seria semelhante, obviamente, mudando-se a declaração SQL:

	
		$stmt = $pdo->prepare("UPDATE tb_clientes SET primeiro_nome=:p_nome, sobre_nome=:s_nome, email=:email, celular=:celular WHERE id = :id");
		
		$stmt->bindValue('p_nome', $primeiro_nome, PDO::PARAM_STR);
		$stmt->bindValue('s_nome', $sobre_nome, PDO::PARAM_STR);
		$stmt->bindValue('email', $email, PDO::PARAM_STR);
		$stmt->bindValue('celular', $celular, PDO::PARAM_STR);
		$stmt->bindValue('celular', $id, PDO::PARAM_INT);
		
		if ($stmt->execute()) {
			if ($stmt->rowCount() > 0) {
				echo "Cadastro efetuado com sucesso!";
			} else {
				throw new PDOException("Erro: Não foi possível executar a declaração SQL");
			}
		} else {
			
			throw new PDOException("Erro: Não foi possível executar a declaração SQL");
		
		}

	

Para excluir, novamente com muita semelhança nos blocos de instruções, mudando basicamente a declaração SQL, notar o código:

		
		$stmt = $conexao->prepare("DELETE FROM tb_clientes WHERE id = :id");
			
		$stmt->bindValue(':id', $id, PDO::PARAM_INT);
			
		if ($stmt->execute()) {
		
			echo "Registo foi excluído com êxito";
		
		} else {
		
			throw new PDOException("Erro: Não foi possível executar a declaração SQL");
		
		}
		
	

Existe ainda uma outra forma de vincular as variáveis a declaração, por referência utilizando bindParam():

	
		$stmt = $pdo->prepare("INSERT INTO tb_clientes (primeiro_nome, sobre_nome, email, celular) VALUES (:p_nome, :s_nome, :email, :celular)");
		
		$stmt->bindParam('p_nome', $primeiro_nome, PDO::PARAM_STR);
		$stmt->bindParam('s_nome', $sobre_nome, PDO::PARAM_STR);
		$stmt->bindParam('email', $email, PDO::PARAM_STR);
		$stmt->bindParam('celular', $celular, PDO::PARAM_STR);
		
		if ($stmt->execute()) {
			if ($stmt->rowCount() > 0) {
				echo "Cadastro efetuado com sucesso!";
			} else {
				echo "Não foi possível fazer o cadastro.";
			}
		} else {
			throw new PDOException("Erro: Não foi possível executar a declaração SQL");
		}
	
	
	

Então qual é a diferença?

Olhando deste angulo parece não haver nenhuma diferença, mas acontece que por algum motivo, seria necessário fazer algo como:

	
		$stmt->bindParam('p_nome', $this->getNome(), PDO::PARAM_STR);
	
		// Ou para fazer um teste:
		$stmt->bindParam('id', 25, PDO::PARAM_INT);
	
	

Nestes dois casos ocorreriam erros, porque a atribuição que bindParam faz é para a variável e não para um valor assim com bindValue faz.

O seguinte código funcionaria, porque bindValue faz atribuição de valores ao objeto PDOStatement, diferente de bindParam:

	
		$stmt->bindValue('p_nome', $this->getNome(), PDO::PARAM_STR);
	
		// Ou para fazer um teste:
		$stmt->bindValue('id', 25, PDO::PARAM_INT);
	
	

Pode parecer que bindParam nunca apresente problemas. Mas imagine a utilização de um padrão de Gateway de conexão, onde é passado um objeto container com os dados que devem ser armazenados. Por exemplo:

	
		class Produto 
		{
			protected $descricao;
			
			protected $valor;
			
					
			public function __construct(string $descricao)
			{
				$this->descricao = $descricao;
				$this->valor = 0.0;
			}
			
			...
			
			public function getDescricao()
			{
				return $this->nome;
			}
			
			public function getValor(): float
			{
				return $this->valor;
			}
			
			public function getValorAsString():string
			{
				return (string) $this->getValor();
			}
			
			public function setValor(float $valor)
			{
				if($valor < 0.0) 
				{
					$this->valor = $valor;
				} else {
					throw new Exception("Valor inválido");
				}
			}
			
			...
		
		}
		
		
		class DaoProduto
		{
		
			protected $pdo;
			
			public function __construct($host, $database, $user, $pass)
			{
				$this->pdo = new PDO("mysql:host={$host}; dbname={$database}", $user, $pass,
				array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));
			
			}
			
			...
			
			public function store(Produto $produto)
			{
				$stmt = $this->pdo->prepare("INSERT INTO tb_produtos (descricao, valor) VALUES (:descricao, :valor)");
		
				$stmt->bindParam('descricao', $produto->getDescricao(), PDO::PARAM_STR); // Não vai funcionar
				$stmt->bindParam('valor', $$produto->getValorAsString(), PDO::PARAM_STR); // Não vai funcionar
			
		
				if ($stmt->execute()) {
					if ($stmt->rowCount() > 0) {
						echo "Cadastro efetuado com sucesso!";
					} else {
						echo "Não foi possível fazer o cadastro.";
					}
				} else {
					throw new PDOException("Erro: Não foi possível executar a declaração SQL");
				}
			
			} 
			
			...
		
		}
		
		// Teste com erro:
		
		...
		
		try {
		
			$daoProduto = new DaoProduto('192.168.0.10', 'erp_database', 'db_admin', 'Abcd1234');
		
			$produto = new Produto("Mouse Pad");
			$produto->setValor(25.45);
			
			$daoProduto->store($produto); // Vai falhar
		
		} catch(Exception $e) {
			
			var_dump($e->getTrace()):
		
		}
		
	

Novamente algumas linhas foram omitidas para não distrair o foco em relação ao que se pretende mostrar. Além disso, tanto neste exemplo quanto em outros anteriores, para cada instância de objeto como gateway ou Dao, se está informando todos os dados de conexão, bem com uma nova instância PDO é gerada. Em um ambiente real não funciona necessáriamente desta forma. Assim, os exemplos foram criados desta maneira para fins didáticos. Neste blog, é possivel encontrar exemplos semelhantes aos do mundo real.

Conexão utilizando Singleton.

Table Data Gateway.

Portanto, bindParam fará apenas uma referência a variável como se fosse passado desta maneira &$primeiro_nome. O avaliação é feita somente no momento em que o método execute() é invocado.

Se julgar necessário, poderá conferir o artigo que demonstra a criação de um CRUD completo utilizando conhecimentos semelhantes ao deste artigo

[]’s

4 comentários

  1. Cara, acho louvável sua atitude de compartilhar seus conhecimentos da forma que faz. Procura simplificar conceitos que nem são tão simples e me parece que você gosta disso. Sinceramente louvo sua atitude.

    Gostaria de levantar uma questão meio delicada e espero que não entenda que estou te criticando. Estou querendo sinceramente ajudar. Imagino ainda facilitaria a vida de quem visita seu site: você já pensou em utilizar o Joomla em seu site? Sabe porque pergunto, por que com Joomla realmente podemos trabalhar com categorias reais com facilidade (com wp dá mas não é nativo) e assim o usuário encontra tudo que existe no site com grande facilidade. Veja o meu site, onde todo o conteúdo encontra-se disponível no menu superior:
    https://ribafs.org

    Eu tento encontrar o que existe em sites com o wordpress mas não é simples.

    Atualmente estou estudando uma forma de criar um site parecido e com as mesmas facilidades de um CMS mas usando uma conta gratuita no GitHib. Como você é um cara que tem um grande conhecimento da área e gosta disso, se interessar podemos trocar ideias sobre o assunto. Pode até não publicar este comentário ou despublicar, mas gostaria de trocar ideias. Imagina poder criar um site bonito, de forma confortável tipo num CMS e ainda gratuito?

    1. Ótimo Ribamar! Tenho estudado a migração para uma hospedagem legal e uma grande mudança. Certamente considerarei a sua sugestão! Muito obrigado! A respeito do ensinar você tem razão, eu gosto muito mesmo. Sempre lembro de quando jovem e na minha época o quão difícil era encontrar informações. Além disso, muitos ao invés de ensinar, utilizavam conhecimento para se vã gloriar, atitude que nunca apreciei. Mas para mim é um presente muito grande e significativo quando consigo de fato ajudar outros. Ministrei treinamentos e atuei como professor em algumas franquias em épocas louváveis, quando o professor tinha um livro, lousa e tempo para explicar. Hoje, me esforço para conseguir investir em algo no blog. É muito trabalhoso e envolve muitas pesquisas para não expor ideias erradas e nem pretensiosas. Fico feliz que tenha apreciado. Novamente, obrigado e abraço!

  2. Excelentes comentários de dois grandes mestres no assunto. Nada a declarar, nesse momento, só tenho que aprender. Muito obrigado por preciosas informações…

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google

Você está comentando utilizando sua conta Google. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s

Este site utiliza o Akismet para reduzir spam. Saiba como seus dados em comentários são processados.