Mysql
 sql >> Teknologi Basis Data >  >> RDS >> Mysql

Alternatif tunggal untuk PHP PDO

Menggunakan pola tunggal (atau antipola) dianggap praktik yang buruk karena membuat pengujian kode Anda sangat sulit dan ketergantungannya sangat berbelit-belit hingga proyek menjadi sulit untuk dikelola di beberapa titik. Anda hanya dapat memiliki satu instance tetap dari objek Anda per proses php. Saat menulis pengujian unit otomatis untuk kode Anda, Anda harus dapat mengganti objek yang digunakan oleh kode yang ingin Anda uji dengan uji ganda yang berperilaku dengan cara yang dapat diprediksi. Ketika kode yang ingin Anda uji menggunakan singleton, maka Anda tidak dapat menggantinya dengan test double.

Cara terbaik (sepengetahuan saya) untuk mengatur interaksi antara objek (seperti Objek Database Anda dan objek lain menggunakan database) adalah dengan membalikkan arah ketergantungan. Itu berarti bahwa kode Anda tidak meminta objek yang dibutuhkannya dari sumber eksternal (dalam banyak kasus yang global seperti metode 'get_instance' statis dari kode Anda) tetapi sebaliknya mendapatkan objek ketergantungannya (yang dibutuhkan) dilayani dari luar sebelum ia membutuhkannya. Biasanya Anda akan menggunakan Depency-Injection Manager/Container seperti ini satu dari proyek symfony untuk menyusun objek Anda.

Objek yang menggunakan objek database akan membuatnya disuntikkan saat konstruksi. Itu dapat disuntikkan baik dengan metode setter atau di konstruktor. Dalam kebanyakan kasus (tidak semua) lebih baik untuk menyuntikkan ketergantungan (objek db Anda) di konstruktor karena dengan cara itu objek yang menggunakan objek db tidak akan pernah dalam keadaan tidak valid.

Contoh:

interface DatabaseInterface
{
    function query($statement, array $parameters = array());
}

interface UserLoaderInterface
{
    public function loadUser($userId);
}

class DB extends PDO implements DatabaseInterface
{
    function __construct(
        $dsn = 'mysql:host=localhost;dbname=kida',
        $username = 'root',
        $password = 'root',
    ) {
        try {
            parent::__construct($dsn, $username, $password, array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'");
            parent::setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        } catch(PDOException $e) {
            echo $e->getMessage();
        }
    }

    function query($statement, array $parameters = array())
    {
        # ...
    }
}

class SomeFileBasedDB implements DatabaseInterface
{
    function __construct($filepath)
    {
        # ...
    }

    function query($statement, array $parameters = array())
    {
        # ...
    }
}

class UserLoader implements UserLoaderInterface
{
    protected $db;

    public function __construct(DatabaseInterface $db)
    {
        $this->db = $db;
    }

    public function loadUser($userId)
    {
        $row = $this->db->query("SELECT name, email FROM users WHERE id=?", [$userId]);

        $user = new User();
        $user->setName($row[0]);
        $user->setEmail($row[1]);

        return $user;
    }
}

# the following would be replaced by whatever DI software you use,
# but a simple array can show the concept.


# load this from a config file
$parameters = array();
$parameters['dsn'] = "mysql:host=my_db_server.com;dbname=kida_production";
$parameters['db_user'] = "mydbuser";
$parameters['db_pass'] = "mydbpassword";
$parameters['file_db_path'] = "/some/path/to/file.db";


# this will be set up in a seperate file to define how the objects are composed
# (in symfony, these are called 'services' and this would be defined in a 'services.xml' file)
$container = array();
$container['db'] = new DB($parameters['dsn'], $parameters['db_user'], $parameters['db_pass']);
$container['fileDb'] = new SomeFileBasedDB($parameters['file_db_path']);

# the same class (UserLoader) can now load it's users from different sources without having to know about it.
$container['userLoader'] = new UserLoader($container['db']);
# or: $container['userLoader'] = new UserLoader($container['fileDb']);

# you can easily change the behaviour of your objects by wrapping them into proxy objects.
# (In symfony this is called 'decorator-pattern')
$container['userLoader'] = new SomeUserLoaderProxy($container['userLoader'], $container['db']);

# here you can choose which user-loader is used by the user-controller
$container['userController'] = new UserController($container['fileUserLoader'], $container['viewRenderer']);

Perhatikan bagaimana kelas yang berbeda tidak saling mengenal. Tidak ada ketergantungan langsung di antara mereka. Hal ini dilakukan dengan tidak memerlukan kelas aktual dalam konstruktor, tetapi memerlukan antarmuka yang menyediakan metode yang dibutuhkannya.

Dengan begitu Anda selalu dapat menulis pengganti untuk kelas Anda dan hanya menggantinya di wadah injeksi ketergantungan. Anda tidak perlu memeriksa seluruh basis kode karena penggantinya hanya perlu mengimplementasikan antarmuka yang sama yang digunakan oleh semua kelas lain. Anda tahu bahwa semuanya akan terus bekerja karena setiap komponen yang menggunakan kelas lama hanya mengetahui tentang antarmuka dan hanya memanggil metode yang diketahui oleh antarmuka.

P.S .:Mohon maafkan referensi konstan saya ke proyek symfony, itu yang paling saya gunakan. Proyek lain seperti Drupal, Propel atau Zend mungkin juga memiliki konsep seperti ini.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. MySQL:Perluas rentang tanggal ke baris baru

  2. Xampp MySQL tidak memulai - Mencoba memulai layanan MySQL...

  3. Menggunakan salah satu kolom dalam kunci komposit sebagai kunci asing

  4. menggunakan if dan else Stored Procedures MySQL

  5. Mengapa ini :id di Rails tidak berfungsi dengan Postgresql tetapi berfungsi dengan MySQL?