Solucionado (ver solução)
Solucionado
(ver solução)
16
respostas

INSERT de várias linhas de um Array

bom dia, estou precisando inserir um array no BD Mysql usando o PDO, esses dados virão via $_POST, o array tem um número de itens que pode variar. A minha dúvida é se devo interar sobre o Array antes de chamar a classe que gravará no BD ou interar sobre o array já nessa classe. Vou usar um exemplo HIPOTÉTICO para facilitar:

persistir.php

public function inserirNomes(){
$nomes = array('joão', 'paulo', 'pedro');
$cargo = "Desenvolvedor"; //esse dado é uma string e não varia para as três linhas do array, ou seja todos sempre serão desenvolvedores.
foreach($nomes AS $nome){
BD::inserir($nome, $cargo);
}
}

DB.php

public static function inserir($nome, $cargo){
    $query = "INSERT INTO tb_funcionarios(nome, cargo) VALUES(:nome, :cargo)";
    $conexao = Conexao::pegarConexao();
        $inserir = $conexao->prepare($query);
        $inserir->bindValue(':nome', $nome);
        $inserir->bindValue(':cargo', $cargo);
        $inserir->execute();
}

outra abordagem:

persistir.php

public function inserirNomes(){
$nomes = array('joão', 'paulo', 'pedro');
$cargo = "Desenvolvedor"; //esse dado é uma string e não varia para as três linhas do array, ou seja todos sempre serão desenvolvedores.

BD::inserir($nomes, $cargo);

}

DB.php

public static function inserir($nomes, $cargo){
    $query = "INSERT INTO tb_funcionarios(nome, cargo) VALUES(:nome, :cargo)";
    $conexao = Conexao::pegarConexao();
        $inserir = $conexao->prepare($query);
foreach($nomes AS $nome){
        $inserir->bindValue(':nome', $nome);
        $inserir->bindValue(':cargo', $cargo);
        $inserir->execute();
}
}

Nas duas abordagens funciona, porém qual delas está de acordo com as boas práticas? se nenhuma delas estiver correta, qual seria melhor?

16 respostas

Marcos, boa tarde!

As duas estão corretas! O que eu acho que vale a pena ressaltar é que cada função deveria ter apenas uma responsabilidade, me parece que iterar sobre o array e inserir cada um deles individualmente não deveria ser uma responsabilidade da função inserir(). Por isso eu pessoalmente deixaria na função inserirNomes() pois deixa claro o que ele faz.

Recomendo fazer nossos cursos de SOLID com PHP.

Espero ter ajudado e bons estudos!

Fala, Marcos. Beleza?

No código hipotético você tem alguns problemas:

  1. Chamadas de métodos estáticos são péssimas na hora de automatizar testes
  2. A cada chamada do método inserir você tá pegando a conexão de novo. =/ O ideal seria injetar essa dependência.
  3. Você está acessando o código que faz acesso a banco diretamente do código que pega os dados do Post. O ideal seria ter uma classe de repositório...

Agora fora todos esses problemas tem 2 soluções possíveis:

  1. Utilizar transações pra envolver os vários inserts que você vai realizar
  2. Montar uma query de INSERT só com todos os valores

A segunda é mais performática mas normalmente suja um pouco mais o código. Aí vai de um arquiteto de software tomar essa decisão.

3 Você está acessando o código que faz acesso a banco diretamente do código que pega os dados do Post. O ideal seria ter uma classe de repositório...

essa classe de repositório faria o que exatamente?

A classe de repositório seria a classe responsável por fazer o acesso à camada de persistência.

Dessa forma você conseguiria utilizar essa classe independente se fosse no código que recebe uma requisição web com $_POST, se viesse de uma API com vc pegando os dados em JSON ou até da linha de comando.

mas nesse exemplo a classe Persistir é que receberia os dados, faria as validações e na sequencia enviaria para a classe BD para inserir no banco, não é assim?

Um Controller receberia a requisição e garantiria que o mínimo dos dados necessários chegaram na requisição.

Algum Application Service criaria uma instância de uma classe de modelo baseado nos dados da requisição (classe Funcionario, por exemplo). Essa classe teria as validações de negócio necessárias.

Esse Application Service teria uma instância de FuncionarioRepository que é a interface que define o contrato das classes responsáveis por tratar a persistência de funcionários.

Uma implementação possível de FuncionarioRepository é PdoFuncionarioRepository que vai ter uma instância de PDO como conexão para o banco.

Outra implementação possível seria DoctrineFuncionarioRepository que utilizaria Doctrine para se conectar.

Poderia existir ApiFuncionarioRepository que não acessa o banco, mas sim uma API que possui a persistência de funcionários.

Dessa forma seu código fica muito mais extensível, entende?

essa parte ainda não consegui absorver... mas passando para outra questão, quanto a injetar a dependencia, posso fazer isso:

    private $conexao;
    public function __construct(){
    $conexao = new Conexao();
        $this->conexao = $conexao->pegarConexao();
    }
public function inserir($nome, $cargo){
    $query = "INSERT INTO tb_funcionarios(nome, cargo) VALUES(:nome, :cargo)";
    $conexao = $this->conexao;
        $inserir = $conexao->prepare($query);
        $inserir->bindValue(':nome', $nome);
        $inserir->bindValue(':cargo', $cargo);
        $inserir->execute();
}

Fala, Marcos.

Cara, sugiro que você faça os cursos de Doctrine (que mostram o que é um repositório) e o de MVC (que fala de padrões e boas práticas, incluindo injeção de dependência).

Mas basicamente, o que você "deveria" estar fazendo é:

class PdoFuncionarioRepository implements FuncionarioRepository
{
    private \PDO $conexao;

    public function __construct(\PDO $conexao)
    {
        $this->conexao = $conexao;
    }

    public function inserir(Funcionario $funcionario): void
    {
        $query = "INSERT INTO tb_funcionarios(nome, cargo) VALUES(:nome, :cargo)";
        $stmt = $conexao->prepare($query);
        $stmt->bindValue(':nome',  $funcionario->nome());
        $stmt->bindValue(':cargo', $funcionario->cargo());
        $stmt->execute();
    }

    // ...
}

Recebendo a conexão por parâmetro você tem diversas vantagens, como por exemplo:

  1. em seus testes automatizados, pode passar uma conexão diferente, com um banco em memória ou um dublê de testes, por exemplo;
  2. Você pode controlar transações da conexão fora do repositório;
  3. Você deixa seu código menos acoplado com a criação da conexão;

primeiro, se inserir o PDO nessa linha

private \PDO $conexao;

o vscode já identifica como erro. segundo, se eu já passar no construtor desssa forma que você mostrou, todas as vezes que instanciar a classe PdoFuncionarioRepository, terei que usar(use) a classe Connexao e passasá-la via parametro, a intenção é essa mesmo?

Vamos lá, Marcos.

Sobre o erro, provavelmente você não está com a versão mais recente do PHP.

Sobre a intenção, não é a classe Conexão que você vai passar. Você vai sempre passar uma instância do PDO. Como essa classe vai ser criada, não importa pro repositório.

Isso te dá flexibilidade pra poder usar conexões diferentes em momentos diferentes.

Quanto a sempre precisar criar a instância, no curso de MVC eu uso uma ferramenta que automatiza a injeção de dependências.

;-)

A minha classe de conexão está assim:

use \PDO;
class Connection{
        public function takeConnection()
        {
                $connect = new \PDO(DB_DRIVE . ':host=' . DB_HOSTNAME . ';dbname=' . DB_DATABASE, DB_USERNAME, DB_PASSWORD);
                $connect->exec("SET CHARACTER SET utf8");
                $connect->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}

eu não posso instanciar essa classe e passá-la como parametro, deve ser a classe PDO?

Marcos, no exemplo que eu montei pra você, a classe espera uma conexão de PDO, não de Connection, então, você precisaria passar uma instância de PDO, entende?

a questão é que estou evitando ao máximo usar moletas, quero fazer tudo, não que eu ache que sou melhor que as pessoas que desenvolveram o Doctrine por exemplo, mas tenho a intenção de aprender PHP e só por enquanto. Em todos os cursos que usaram o Doctrine por exemplo, fiquei perdido e saí mais confuso do que entrei.

Então, Marcos, enquanto você não aprende esses pontos sobre uso de ferramentas, pode utilizar um método que cria conexões e sempre passar o retorno deste método para o construtor dos repositórios. 0 problemas.

Quanto aos cursos de Doctrine, abre uma dúvida aqui no fórum com o que vc não entendeu que eu tento te ajudar. :-D

você poderia mostrar um exemplo de como fazer passando uma instancia de PDO?

solução!

Fala, Marcos! Saiu hoje esmo um novo curso de PDO. Lá eu falo um pouco de injeção de dependência.

;-)