PHP - лучший способ загрузить объект базы данных из модели, но иметь только один экземпляр? - PullRequest
8 голосов
/ 02 марта 2012

Это моя проблема, у меня есть крошечная PHP MVC-инфраструктура, которую я построил.Теперь, когда я в контроллере, я должен быть в состоянии загрузить модель.У меня есть Модели, названные как таблицы базы данных, такие как Users.php, Pages.php и т. Д.

Все контроллеры расширяют BaseController, а все Модели расширяют BaseModel, так что я могу иметь некоторые методы, доступные для всех контроллеров.Как и в любом контроллере, я могу использовать метод loadModel следующим образом:

$usersModel = $this->loadModel("Users");

Теперь $ usersModel будет объектом, представляющим таблицу базы данных пользователей, и оттуда я должен открыть соединение с базой данных, а также получить, обновить, удалить данные изТаблица пользователей.

Чтобы получить соединение с базой данных из моих Моделей, baseModel имеет метод loadDatabase (), и я использую его так:

$database = $this->loadDatabase();

Это вернет класс, который является тонким слоем вокруг PDO, такоттуда я могу использовать что-то вроде этого:

$data = $database->fetchAssoc("SELECT * FROM users WHERE name = ?", array("someone"));

Вот так я бы вернул некоторые данные из $ Model из контроллера в контроллер.

Итак, по сути, контроллер может загрузить модель, а затемвызовите методы для этой модели, которые возвращали бы некоторые данные или обновляли или удаляли и т. д.

Теперь контроллер может загрузить более одной модели.И каждая модель должна загружать базу данных, и тут все становится сложнее.Мне нужно только одно соединение с базой данных.

Так выглядит метод loadDatabase:

    public function loadDatabase()
    {
        // Get database configuration  
        require_once("Application/Config/Database.php");

        // Build configuration the way Database Object expects it
        $dns      = "{$db_config['db_driver']}:host={$db_config['db_host']};dbname={$db_config['db_name']}";
        $username = $db_config['db_user'];
        $password = $db_config['db_pass'];
        $options  = $db_config['db_options'];

        // Return new Database object
        return new \Core\Database\Database($dns, $username, $password, $options);

    }

Поэтому, прежде чем загрузить объект базы данных, я должен загрузить конфигурацию для соединения с базой данных, как объекты базы данных.__конструктор ожидает этого.Затем я создаю новый объект базы данных и возвращаю его.

Теперь я застрял и не знаю, является ли это правильным способом загрузки базы данных из модели?Как мне это настроить, как мне загрузить базу данных изнутри модели, чтобы всегда был только один экземпляр объекта базы данных.Из-за того, что я делаю что-то подобное из Controller:

$users = $this->loadModel("Users");
$pages = $this->loadModel("Pages");

$users->doSomethingThatNeedsDatabase();
$users->doSomethingThatNeedsDatabase();
$pages->doSomethingThatNeedsDatabase();

Я бы создал 3 объекта базы данных: (

Поэтому мой вопрос: как мне загрузить базу данных изнутри моделей и какдолжен ли этот метод выглядеть в BaseModel? Что если я хотел бы использовать 2 базы данных, у меня возникнут большие проблемы с этой настройкой. Как я могу добиться этого, не используя шаблон синглтона?

На данный момент у меня также естьчто-то вроде этого:

public function getDatabase()
{

    $reg = \Core\Registry\Registry::getInstance();
    $db = $reg->getObject("database");
    if(!isset($db))
    {
        require_once("Application/Config/Database.php");

        $dns      = "{$db_config['db_driver']}:host={$db_config['db_host']};dbname={$db_config['db_name']}";
        $username = $db_config['db_user'];
        $password = $db_config['db_pass'];
        $options  = $db_config['db_options'];                

        $db = new \Core\Database\Database($dns, $username, $password, $options); 

        $reg->setObject('database', $db);
    }
    return $reg->getObject('database');

}

Это шаблон реестра, в котором я могу хранить общие объекты. Поэтому, когда Модель запрашивает подключение к БД, я могу проверить, находится ли класс БД в реестре, и вернуть его, если нетinstaciate, а затем вернуть ... Самое странное, что мне нужно загрузить конфигурационный массив ...

Так каков наилучший способ загрузки базы данных из моделей?

Спасибо за чтениеэто был очень длинный вопрос, но это меня так сильно беспокоит, я надеюсь, что кто-то может помочь мне с этим ... Спасибо!

Ответы [ 3 ]

11 голосов
/ 14 марта 2012

Вы идете не так, чтобы решить эту проблему.

Вместо того, чтобы каждый раз вручную создавать новую «Модель» и затем настраивать ее, вы должны создать структуру, которая сделает это за вас (чрезвычайно упрощенная версия):

class ModelFactory
{
    protected $connection = null;
    // --- snip --

    public function __construct( $connection )
    {
        $this->connection = $connection;
    }
    // --- snip --

    public function buildMapper( $name )
    {
        $instance = new {$name}( $this->connection );
        return $instance;
    }
    // --- snip --

}

Этот класс вы будете использовать в index.php или bootstrap.php или в любом другом файле, который вы используете в качестве точки входа для своего приложения:

// at the bootstrap level
$modelFactory = new ModelFactory( new PDO(...) );

// i am assuming that you could get $controllerName 
// and $action from routing mechanism
$controller = new {$controllerName}( $modelFactory );
$controller->{$action}();

Основная проблема, которую вы имеете, на самом деле вызвана неправильным пониманием того, что такое Модель. В надлежащем MVC Модель - это слой, а не определенный класс. Слой модели состоит из множества классов / экземпляров с двумя основными обязанностями:

  • бизнес-логика домена
  • доступ к данным и их хранение

Экземпляры в первой группе обычно называются Доменные объекты или Бизнес-объекты (что-то вроде гиков и ботанов ). Они имеют дело с проверками, вычислениями, различными условиями, но не имеют понятия, как и где хранится информация. Это не меняет способ создания Invoice , независимо от того, поступают ли данные из SQL, удаленного REST API или снимка экрана документа MS Word.

Другая группа состоит в основном из Data Mappers . Они хранят и извлекают информацию из доменных объектов. Обычно это где ваш SQL будет. Но преобразователи не всегда отображаются напрямую в схему БД. В классической структуре «многие ко многим» у вас может быть 1, 2 или 3 картографа, обслуживающих хранилище. Картографы обычно по одному на каждый объект домена ... но даже это не обязательно.

В контроллере все это будет выглядеть примерно так.

public function actionFooBar()
{
    $mapper = $this->modelFactory->buildMapper( 'Book' );
    $book = $this->modelFactory->buildObject( 'Book' );
    $patron = $this->modelFactory->buildObject( 'Patron' );

    $book->setTitle('Neuromancer');
    $mapper->fetch( $book );

    $patron->setId( $_GET['uid']);

    if ( $book->isAvailable() )
    {
        $book->lendTo( $user );
    }

    $mapper->store( $book );

}

Может быть, это даст вам некоторые указания о том, в каком направлении его брать.

Некоторые дополнительные видеоматериалы:

2 голосов
/ 02 марта 2012

Лучший способ для этих моделей использовать шаблон внедрения зависимостей.

public function loadModel() {
    $db = $this->loadDatabase();
    $model = new Model();
    $model->setDatabase($db);
    return $model;
}

, где loadDatabase() должен один раз инициировать и после возвращать простой экземпляр подключения к базе данных.

0 голосов
/ 02 марта 2012

Попробуйте:

class Registry
{
    /** @return Registry */
    public static function getInstance() {}

    public function getObject($key) {}
    public function setObject($key, $value) {}

    /** @return bool */
    public function isObjectExists($key) {}
}

class Db
{
    const DB_ONE = 'db_one';
    const DB_TWO = 'db_two';

    protected static $_config = array(
        self::DB_ONE => array(),
        self::DB_TWO => array(),
    );

    public static function getConnection($dbName) {}
}


abstract class BaseModel
{
    abstract protected function _getDbName();

    protected function _getConnection()
    {
        $dbName = $this->_getDbName();

        if (!Registry::getInstance()->isObjectExists($dbName)) {
            Registry::getInstance()->setObject($dbName, Db::getConnection($dbName));
        }

        return Registry::getInstance()->getObject($dbName);
    }
}

class UserModel extends BaseModel
{
    protected function _getDbName()
    {
        return Db::DB_ONE;
    }
}

class PostModel extends BaseModel
{
    protected function _getDbName()
    {
        return Db::DB_TWO;
    }
}

Удачи!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...