Como configurar corretamente uma conexão PDO

92

De vez em quando, vejo perguntas relacionadas à conexão com o banco de dados.
A maioria das respostas não é como eu faço, ou posso simplesmente não obter as respostas corretamente. De qualquer forma; Nunca pensei nisso porque a maneira como faço funciona para mim.

Mas aqui está um pensamento maluco; Talvez eu esteja fazendo tudo errado, e se for o caso; Eu realmente gostaria de saber como conectar corretamente a um banco de dados MySQL usando PHP e PDO e torná-lo facilmente acessível.

É assim que estou fazendo:

Em primeiro lugar, aqui está minha estrutura de arquivo (reduzida) :

public_html/

* index.php  

* initialize/  
  -- load.initialize.php  
  -- configure.php  
  -- sessions.php   

index.php
No topo, eu tenho require('initialize/load.initialize.php');.

load.initialize.php

#   site configurations
    require('configure.php');
#   connect to database
    require('root/somewhere/connect.php');  //  this file is placed outside of public_html for better security.
#   include classes
    foreach (glob('assets/classes/*.class.php') as $class_filename){
        include($class_filename);
    }
#   include functions
    foreach (glob('assets/functions/*.func.php') as $func_filename){
        include($func_filename);
    }
#   handle sessions
    require('sessions.php');

Sei que existe uma maneira melhor, ou mais correta, de incluir aulas, mas não consigo lembrar qual era. Não tive tempo para investigar ainda, mas acho que era algo com autoload. algo parecido...

configure.php
Aqui eu basicamente substituo algumas propriedades do php.ini e faço alguma outra configuração global para o site

connect.php
Eu coloquei a conexão em uma classe para que outras classes possam estender esta ...

class connect_pdo
{
    protected $dbh;

    public function __construct()
    {
        try {
            $db_host = '  ';  //  hostname
            $db_name = '  ';  //  databasename
            $db_user = '  ';  //  username
            $user_pw = '  ';  //  password

            $con = new PDO('mysql:host='.$db_host.'; dbname='.$db_name, $db_user, $user_pw);  
            $con->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
            $con->exec("SET CHARACTER SET utf8");  //  return all sql requests as UTF-8  
        }
        catch (PDOException $err) {  
            echo "harmless error message if the connection fails";
            $err->getMessage() . "<br/>";
            file_put_contents('PDOErrors.txt',$err, FILE_APPEND);  // write some details to an error-log outside public_html  
            die();  //  terminate connection
        }
    }

    public function dbh()
    {
        return $this->dbh;
    }
}
#   put database handler into a var for easier access
    $con = new connect_pdo();
    $con = $con->dbh();
//

Aqui, eu acredito que há espaço para melhorias massivas desde que comecei recentemente a aprender OOP e usar PDO em vez de mysql.
Então, eu apenas segui alguns tutoriais para iniciantes e experimentei coisas diferentes ...

sessões.php
Além de lidar com sessões regulares, também inicializo algumas classes em uma sessão como esta:

if (!isset($_SESSION['sqlQuery'])){
    session_start();
    $_SESSION['sqlQuery'] = new sqlQuery();
}

Dessa forma, essa aula está disponível em todos os lugares. Isso pode não ser uma boa prática (?) ...
De qualquer forma, é isso que essa abordagem me permite fazer de qualquer lugar:

echo $_SESSION['sqlQuery']->getAreaName('county',9);  // outputs: Aust-Agder (the county name with that id in the database)

Dentro de my sqlQuery- class , que extendsmy connect_pdo- class , tenho uma função pública chamada getAreaNameque trata da solicitação para meu banco de dados.
Muito legal, eu acho.

Funciona como um encanto
Então é basicamente assim que estou fazendo.
Além disso, sempre que preciso buscar algo do meu banco de dados que não esteja dentro de uma classe, simplesmente faço algo semelhante a isto:

$id = 123;

$sql = 'SELECT whatever FROM MyTable WHERE id = :id';
$qry = $con->prepare($sql);
$qry -> bindParam(':id', $id, PDO::PARAM_INT);
$qry -> execute();
$get = $qry->fetch(PDO::FETCH_ASSOC);

Como coloquei a conexão em uma variável dentro de connect_pdo.php , acabei de me referir a ela e estou pronto para continuar. Funciona. Recebo os resultados esperados ...

Mas, independentemente disso; Eu realmente apreciaria se vocês pudessem me dizer se estou longe daqui. O que devo fazer em vez disso, áreas que posso ou devo mudar para melhoria etc ...

Estou ansioso para aprender ...

ThomasK
fonte
9
Você deve usar um autoloader em vez de incluir todos os arquivos em seu aplicativo de uma vez.
Lusitano
4
Esta pergunta é provavelmente melhor na Code Review
Madara's Ghost

Respostas:

105

O objetivo

A meu ver, seu objetivo neste caso é duplo:

  • criar e manter uma conexão única / reutilizável por banco de dados
  • certifique-se de que a conexão foi configurada corretamente

Solução

Eu recomendaria usar a função anônima e o padrão de fábrica para lidar com a conexão PDO. O uso seria assim:

$provider = function()
{
    $instance = new PDO('mysql:......;charset=utf8', 'username', 'password');
    $instance->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $instance->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
    return $instance;
};

$factory = new StructureFactory( $provider );

Em seguida, em um arquivo diferente ou inferior no mesmo arquivo:

$something = $factory->create('Something');
$foobar = $factory->create('Foobar');

A própria fábrica deve ser parecida com isto:

class StructureFactory
{
    protected $provider = null;
    protected $connection = null;

    public function __construct( callable $provider )
    {
        $this->provider = $provider;
    }

    public function create( $name)
    {
        if ( $this->connection === null )
        {
            $this->connection = call_user_func( $this->provider );
        }
        return new $name( $this->connection );
    }

}

Dessa forma, você teria uma estrutura centralizada, o que garante que a conexão seja criada apenas quando necessária. Também tornaria o processo de teste de unidade e manutenção muito mais fácil.

O provedor, neste caso, seria encontrado em algum lugar no estágio de bootstrap. Essa abordagem também forneceria um local claro onde definir a configuração que você usa para se conectar ao banco de dados.

Lembre-se de que este é um exemplo extremamente simplificado . Você também pode se beneficiar assistindo aos dois vídeos a seguir:

Além disso, eu recomendo fortemente a leitura de um tutorial adequado sobre o uso de PDO (há um registro de tutoriais ruins online).

Tereško
fonte
3
Já que mesmo o PHP5.3 está se aproximando do fim do prazo. A maioria dos sites com versões desatualizadas de PHP são, na verdade, apenas hospedagem barata para Wordpress. O impacto de ambientes pré-5.3 no desenvolvimento profissional (eles meio que se beneficiariam de trechos como este) é insignificante, na minha estimativa.
tereško
5
@thelolcat eu concordo com você. Ele é mais ou menos a mesma resposta. Isso se você não perceber o fato de que é completamente diferente.
PeeHaa
1
@thelolcat, então você deve aprender o que é injeção de dependência . Em vez de continuar a se envergonhar. Curiosamente, o segundo vídeo no post acima (intitulado: "Don't Look For Things" ) na verdade explicou o que é DI e como usá-lo ... mas é claro que você é muito avançado para coisas triviais.
tereško
2
Esta é uma resposta antiga, mas uma boa - e um ótimo link para Mysql pdo no final
Strawberry
1
@teecee, você deve começar aprendendo primeiro como usar o PDO. Eu recomendaria este tutorial: wiki.hashphp.org/PDO_Tutorial_for_MySQL_Developers , porque ele é feito exatamente para pessoas que desejam migrar de mysql_*para PDO. Então você pode voltar e olhar para esta solução, que é voltada para aqueles que já usam PDO, mas precisam de uma forma de compartilhar a conexão do banco de dados entre várias classes.
tereško
24

Eu sugeriria não usar $_SESSIONpara acessar sua conexão DB globalmente.

Você pode fazer algumas coisas (da pior para as práticas recomendadas):

  • Acesse $dbhusando global $dbhdentro de suas funções e classes
  • Use um registro singleton e acesse-o globalmente, assim:

    $registry = MyRegistry::getInstance();
    $dbh = $registry->getDbh();
  • Injete o manipulador de banco de dados nas classes que precisam dele, assim:

    class MyClass {
        public function __construct($dbh) { /* ... */ }
    }

Eu recomendo o último. É conhecido como injeção de dependência (DI), inversão de controle (IoC) ou simplesmente o princípio de Hollywood (não ligue para nós, nós ligaremos para você).

No entanto, é um pouco mais avançado e requer mais "fiação" sem uma estrutura. Portanto, se a injeção de dependência for muito complicada para você, use um registro singleton em vez de um monte de variáveis ​​globais.

Ian Unruh
fonte
Então, eu acesso minha conexão db globalmente quando configuro minha sqlQuery-class em sessão, pois ela estende connect_pdo?
ThomasK
7

Recentemente, cheguei a uma resposta / pergunta semelhante por conta própria. Isso é o que eu fiz, caso alguém se interesse:

<?php
namespace Library;

// Wrapper for \PDO. It only creates the rather expensive instance when needed.
// Use it exactly as you'd use the normal PDO object, except for the creation.
// In that case simply do "new \Library\PDO($args);" with the normal args
class PDO
  {
  // The actual instance of PDO
  private $db;

  public function __construct() {
    $this->args = func_get_args();
    }

  public function __call($method, $args)
    {
    if (empty($this->db))
      {
      $Ref = new \ReflectionClass('\PDO');
      $this->db = $Ref->newInstanceArgs($this->args);
      }

    return call_user_func_array(array($this->db, $method), $args);
    }
  }

Para chamá-lo, você só precisa modificar esta linha:

$DB = new \Library\PDO(/* normal arguments */);

E a sugestão de tipo se você estiver usando para (\ Library \ PDO $ DB).

É muito semelhante à resposta aceita e à sua; no entanto, tem uma vantagem notável. Considere este código:

$DB = new \Library\PDO( /* args */ );

$STH = $DB->prepare("SELECT * FROM users WHERE user = ?");
$STH->execute(array(25));
$User = $STH->fetch();

Embora possa parecer PDO normal (ele muda \Library\apenas por isso ), ele na verdade não inicializa o objeto até que você chame o primeiro método, seja ele qual for. Isso o torna mais otimizado, uma vez que a criação do objeto PDO é um pouco cara. É uma classe transparente, ou o que é chamado de Ghost , uma forma de Lazy Loading . Você pode tratar o $ DB como uma instância normal do PDO, passando-o adiante, fazendo as mesmas operações, etc.

Francisco presencia
fonte
Isso é chamado de "padrão de decorador"
Yang
0
$dsn = 'mysql:host=your_host_name;dbname=your_db_name_here'; // define host name and database name
    $username = 'you'; // define the username
    $pwd='your_password'; // password
    try {
        $db = new PDO($dsn, $username, $pwd);
    }
    catch (PDOException $e) {
        $error_message = $e->getMessage();
        echo "this is displayed because an error was found";
        exit();
}
hi-code
fonte