Avoid permanent connections and Singleton class in a PHP project

Asked

Viewed 482 times

2

I am developing a CMS with PHP and using the MVC standard, without using frameworks, I know many will say that I should not do this, but I am doing this as a form of study.

However, I never really understood some concepts, such as the functioning of permanent connections. I thought there would be performance gains when using a Singleton class with a permanent connection to my database, apparently I was wrong and found many topics here at Stackoverflow that recommend avoid using this standard, by creating dependencies in other classes.

Then the question arose to me, how could I have a permanent connection to a database which would have to inject my connection by parameters to some model whenever I need? This would not instantiate a new object and consecutively create a new connection?

Edit: My connection is made through a class that extends the PDO library.

  • No matter how many questions you ask, and with each answer you receive, you won’t be able to understand exactly where and when they should be used, so I recommend that you try before, and only then question. This is because, even if someone explains it to you and gives you an example now, it is very likely that you will continue unanswered, this because this type of pattern is both unique and common depending on the situation.

  • My fear is just to wear it and regret it later..

  • "but I’m doing it as a form of study" - This is the best time for testing, what works and how it works and what the drawbacks of using this standard in this app. And using the DI, you’d have to rewrite a sizeable portion of your script.

  • The code itself is quite small, I’m trying to do with low coupling, so I guess I won’t have so much work. But seeing as how Singleton doesn’t work the way I’d hoped, I kind of abandoned him. I don’t want my views to have access to my database, you know? In this case I prefer something more encapsulated.

  • "But seeing as how Singleton doesn’t work the way I’d hoped" - It’s not to work the way you expect it to, it’s going to work the way it works, that’s what patterns do, they’re used to solve specific, very specific problems that the programmer encounters during the construction of the project. They are a kind of compact logic, not exactly applicable in any situation. Talks of "permanent connection", and referes CMS, but you cite a pattern that has other responsibilities. This is basically what I’m talking about.

2 answers

2


First, it turns out that the "standards are not mandatory", in other words, they are not the rule for anything, they are a solution to common problems encountered during the development of a project. They are like tools that we should use only when necessary, not when we think necessary or even because we think we should use them.

Injecting a connection into a class is the same as injecting a connection instance into a class. And according to Wikipédia ...

Dependency injection (Dependency Injection) is a standard of computer program development used when it is necessary to maintain a low level of coupling between different modules of a system. In this solution the dependencies between modules are not defined programmatically, but rather by the configuration of a software infrastructure (container) that is responsible for "injecting" into each component its declared dependencies. Dependency injection is related to the pattern Inversion of control but cannot be considered a synonym of this. It is also directly associated with Ioc as mentioned above and Strategy Patterns. «1»

A dependency can be seen as a consumable service, and an injection as a consumed service.

Example: To use the services of a particular operator, we put a chip of the same operator on the mobile phone and this phone provides the network and the services, and we consume these services whatever they are, depending on the chip used. In that case we would have the mobile phone as container, responsible for introducing these services to the user.

Like any other design pattern, these do not escape logic, if used when unnecessary becomes a nuisance, the code becomes too complex at the expense of nothing and who knows what other problems may cause. And from what I see is clearly what will happen to you. By choosing to extend the class PDO instead of initiating an instance in a constructor as is common, it is clear that you wanted something simple, and a CMS is basically an application aimed at CRUD and other basic operations. I think if you use a normal connection class there won’t be any complication.

It is one thing to want to learn and another to use unnecessarily. As far as I’m concerned, if you want to learn how to work with Patterns, I recommend looking for something more focused on Patterns to work with, rather than investing unnecessary resources in something you can do at no cost, if you know what I mean.

When it’s convenient ?

  • You need to inject configuration data into one or more components.
  • You need to inject the same dependency into multiple components.
  • You need to inject different implementations of the same dependency.
  • You need to inject the same implementation into different configurations.
  • You need some of the services provided by the container.

Without further ado, I think it’s time to move on to some examples of use, but if you want to dig deeper into injections, and also know what disadvantages they have, or even points for me omitted, recommend you read these articles:

Or you could just use the Google for more results. Also, if you find it unclear, analyze the code of a framework anyone who has any of these standards implemented, so you can see how they are applied in production.

Example without an injection:

<?php
#classe de conexão que estende a classe PDO
class Database extends PDO
{
    private $data;

    public function __construct()
    {
        # @Configuracao
        $dsn = 'mysql:host=localhost;dbname=exemplo;charset=utf8;';
        $param = array('dsn'=>$dsn, 'user'=>'root', 'pwd'=>'');

        if(!empty($param)){
            try{
                parent::__construct($param['dsn'], $param['user'], $param['pwd']);
            } catch(PDOException $e){
                die($e->getMessage());
            }
        }
    }

    public function select($tabela, $args=null){
        if(!empty($tabela)){
            if(!empty($args)){
                $sql = "SELECT * FROM {$tabela} WHERE {$args[0]} {$args[1]} {$args[1]}";
            } else {
                $sql = "SELECT * FROM {$tabela}";
            }
            if($this->data = parent::query($sql)){
                return $this->data;
            }
        }
        return false;
    }
}

?>

Class of consumer of connection

<?php

require_once 'class.database.php';

class Books
{
    private $_db;
    public function __construct()
    {
        $this->_db = new Database();
    }

    public function fetchAll($tabela)
    {
        if($query = $this->_db->select($tabela)){
            if($resultado = $query->fetchAll(PDO::FETCH_OBJ));
            return $resultado;
        }
        return false;
    }
}

$instancia = new Books();
$livros = $instancia->fetchAll('exemplo');
foreach($livros as $livro){
    print "ID: #" . $livro->id . "<br/>";
    print "Titulo: " . $livro->titulo . "<br/>";
}

?>

Example with injection:

<?php

include_once 'class.container.php';

class Books
{
    private $_db;
    // Construtor
    public function __construct(Container $param)
    {
        $this->_db = $param->Connect();
    }

    // Método que faz uso dessa instancia
    public function getAll($tabela)
    {
        $sql = "SELECT * FROM {$tabela}";
        if($query = $this->_db->query($sql)){
            while($resultado = $query->fetchAll(PDO::FETCH_OBJ)){
                return $resultado;
            }
        }
        return false;
    }
}

# @Books exemplo
# @Configuração:
$param = array('dsn'=>'mysql:host=localhost;dbname=exemplo;charset=utf8;',
               'user'=>'root',
               'pwd'=>'');
# Injecção do @Container na classe @Books              
$livros = new Books(new Container($param));

# Retornar os resultados da tabela @exemplo
$resultados = $livros->getAll('exemplo');
foreach($resultados as $resultado){
    print $resultado->titulo . "<br/>";
}

?>

This would be the container, using a builder.

<?php

class Container
{
    private $instancia, $params;

    public function __construct($params)
    {
        $this->params = $params;
    }
    public function Connect()
    {
        if(empty($this->instancia['db']))
        {
            $this->instancia['db'] = new PDO($this->params['dsn'], 
                                             $this->params['user'], 
                                             $this->params['pwd']
                                             );
        }
        return $this->instancia['db'];
    }
}

?>

These past examples, are very simple implementations of this pattern, usually create more complex routes to work more deliberately with different configurations in various situations. These configurations that are there, are something that is almost not seen in a deeper implementation of the standard.

In the end, I don’t know if I’ve made one or a few mistakes somewhere, but this is it, the most comprehensive answer I’ve been able to give.

1

If you are using the MVC standard I recommend that you create an Abstract model that will be extended by all other models. In this abstract model you can implement methods like open connection, close connection, run query, recover results, start, vomit and rollback transactions.

So all your models will have access to the bank.

PS.: The way you’re going to make the connection is very inherent in the architecture you want to use. Well, that’s my opinion.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.