Правильно ли вызывать базу данных из Model в приложении MVC? - PullRequest
10 голосов
/ 05 мая 2011

Я создаю крошечный MVC-фреймворк для обучения / экспериментов и для небольших проектов. Мне нужно было разобраться в основах модели, поскольку полная MVC-инфраструктура и ORM излишни всего за несколько обращений к базе данных.

Class Model
{
}

Используя пустой класс, где мне нужно вызывать объект new PDO для вызовов базы данных?

Как будет выглядеть запрос внутри модели?

Кроме того, где я могу найти веб-ресурс / книгу для начинающих в MVC (с большим количеством примеров кода)? Я слышал много таких терминов, как бизнес-логика и логика базы данных. Я помню, как читал где-то, что вы должны разделить бизнес-логику и логику базы данных. Я могу немного понять концепцию, мне просто интересно, как она выглядит или что она означает в самом коде. Я запутался в том, как следует разделять бизнес-логику и логику базы данных, но все же находиться внутри модели.

В основном я ищу примеры кода / логики в качестве ответов, за исключением, может быть, последнего абзаца.

Ответы [ 4 ]

21 голосов
/ 06 мая 2011

Предупреждение:
Информация в этих сообщениях крайне устарела .Это представляет мое понимание шаблона MVC, как это было более 2 лет назад.Это будет обновлено, когда я доберусь до этого.Вероятно, в этом месяце (2013.09).

Черт возьми!(2017,11).

Model сам по себе не должен содержать любой SQL.Когда-либо.Предполагается, что он содержит только бизнес-логику домена.

Подход, который я бы рекомендовал, состоит в том, чтобы разделить обязанности, которые не являются строго «бизнес-логикой», на два других набора конструкций: Объекты домена и Data Mappers .

Например, если вы ведете блог, то модель не будет опубликована.Вместо этого наиболее вероятно, что модель будет Blog, и эта модель будет работать с несколькими Domain Objects: несколькими экземплярами Post, Comment, User и, возможно, другими объектами.

В вашей модели доменные объекты не должны знать, какхранить себя в базе данных.Или даже знать о существовании любой формы хранения.Это ответственность Data Mappers.Все, что вам нужно сделать в модели, это позвонить $mapper->store( $comment );.И преобразователь данных должен знать, как хранить один конкретный тип объектов домена, и выиграть, в какую таблицу помещать информацию (обычно хранение одного объекта домена фактически влияет на несколько таблиц).


Некоторый код

(только соответствующие фрагменты из файлов):

  • Полагаю, вы знаете, как написать хороший конструктор .. если у вас есть сомнения, прочитайте эту статью
  • в примере ничего не указано в пространстве имен, но должно быть
  • все, что начинается с _ в примере: protected

от /application/bootstrap.php

/* --- snip --- */

$connection = new PDO( 'sqlite::memory:' );
$model_factory = new ModelFactory( $connection );

$controller = new SomeController( $request , $model_factory );

/* --- snip --- */

$controller->{$action}();

/* --- snip --- */
  • Контроллеру не нужно знать о подключении к базе данных.
  • Если вы хотите изменить подключение к БД для всего приложения, вам нужно изменить одну строку
  • чтобы изменить способ создания моделей, вы создаете другой класс, который реализует тот же интерфейс, что и ModelFactory

из /framework/classes/ModelFactory.php

/* --- snip --- */

class ModelFactory implements ModelBuilderInterface
{
   /* --- snip --- */

   protected function _prepare()
   {
      if ( $this->_object_factory === null  )
      {
         $this->_object_factory = new DomainObjectFactory;
      }
      if ( $this->_mapper_factory === null )
      {
         $this->_mapper_factory = new DataMapperFactory( $this->_connection );
      }
   }

   public function build( $name )
   {
      $this->_prepare();
      return new {$name}( $this->_object_mapper , $this->_data_mapper );
   }

   /* --- snip --- */

}
  • только картографы данныхбудет использовать базу данных, подключение требуется только фабрике картографических данных
  • все зависимости модели вводятся в конструктор
  • каждый экземпляр DataMapper в приложении использует одно и то же соединение с БД, нет Global State (видео) требуется.

файл /application/controllers/SomeController.php

/* --- snip --- */

   public function get_foobar()
   {
      $factory = $this->_model_factory;
      $view = $this->_view;

      $foo = $factory->build( 'FooModel' );
      $bar = $factory->build( 'BarModel' );

      $bar->set_language( $this->_request->get('lang') );

      $view->bind( 'ergo' , $foo );

      /* --- snip --- */

   }

/* --- snip --- */
  • контроллер не знает подробностей создания модели

файл /application/models/FooModel.php

/* --- snip --- */

   public function find_something( $param  , $filter )
   {
      $something = $this->_object_factory('FooBar');
      $mapper = $this->_mapper_factory('FooMapper');

      $something->set_type( $param );
      $mapper->use_filter( $filter )->fetch( $something );

      return $something;
   }

/* --- snip --- */
  • объект доменаотвечает за проверку заданных параметров
  • представление получает и решает, как его представить
  • картограф берет объект и помещает в него всю необходимую информацию из хранилища (это не обязательно БД.. это может быть взято из некоторого файла или внешнего REST API)

Я надеюсь, что это поможет вам понять разделение между логикой БД и бизнес-логикой (и фактически, логикой представления вo)


Несколько замечаний

Модель никогда не должна расширять базу данных или ORM, поскольку модель не является их подмножеством.Расширяя класс, вы объявляете, что он обладает всеми характеристиками суперкласса, но с небольшими исключениями.

class Duck extends Bird{}
class ForestDuck extends Duck{}
// this is ok

class Table extends Database{}
class Person extends Table{}
// this is kinda stupid and a bit insulting

Помимо очевидных логических проблем, если ваша Модель тесно связана с лежащей в основе базой данных, она делаетКод чрезвычайно сложно протестировать (речь идет о Unit Testing (видео) ).


Лично я считаю, что ORM бесполезны, а в больших проектах - даже вредны.Проблема связана с тем, что ORM пытаются соединить два совершенно разных способа решения проблем: ООП и SQL.

Если вы начнете проект с ORM, то после короткого периода обучения вы сможете очень быстро писать простые запросы. Но к тому времени, когда вы начинаете сталкиваться с ограничениями и проблемами ORM, вы уже полностью инвестируете в использование ORM (может быть, даже были наняты новые люди, которые действительно хорошо разбирались в вашем избранном, но были поглощены простым SQL ). Вы попадаете в ситуацию, когда каждая проблема, связанная с БД, занимает все больше и больше времени. А если вы использовали ORM на основе шаблона ActiveRecord, то проблемы напрямую влияют на ваши модели.

Дядя Боб называет это "техническим долгом".


Несколько книг

слабо связанный с предметом

1 голос
/ 05 мая 2011

По моему опыту, разные фреймворки интерпретируют MVC несколько свободно и обычно с некоторыми отклонениями. Однако они обычно соглашаются, что MVC делится следующим образом:

  • Модель - Бизнес-логика + Хранение данных / поиск / структурирование
  • Просмотр - представление данных
  • Контроллер - вызов методов для модели после анализа запроса

Я часто использую Symfony и могу привести несколько небольших примеров. Имейте в виду, это очень упрощено. : Р

Модель:

class MyUnitTable extends Doctrine_Table
{
    // .. various other pieces of code added by Symfony ... //
    public function getForAccount( Account $_account ) {
    $q = $this
         ->createQuery('e')
         ->leftJoin('e.User u')
         ->leftJoin('u.Profile p')
         ->leftJoin('p.Account a')
         ->where('a.accountid = ?', $_account->getAccountid());
    return $q->execute();
    }
}

class myUnitActions extends sfActions
{
    public function executeIndex(sfWebRequest $request)
    {
        $this->units = Doctrine_Core::getTable('MyUnit')->getForAccount($foo);
    }
}

Вид (просто показывает фрагмент здесь):

<div id="my-unit-list-container">
    <ul>
    <?php foreach( $units as $unit ): ?>
        <li><?php echo $unit->getName(); ?></li>
    <?php endforeach; ?>
    </ul>
</div>

Контроллер:

Контроллер - это место, где обрабатывается запрос. Он проанализирует запрос и выяснит, какое действие (например, myUnitActions :: executeIndex () сверху) в модели, которую следует вызвать.

Последние заметки:

Я уверен, что вы можете видеть, что происходит в приведенном выше коде (ORM добавляет много удобства). У вас есть контроллер, отправляющий запрос в модель, модель работает с вашей проблемной областью, а также фактически извлекает данные из базы данных и, наконец, представление, отображающее данные.

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

Превосходным чтивом для вас будет руководство 21 Days With Jobeet от людей, стоящих за Symfony. Это хорошая работа.

Вы также должны взглянуть на код для Symfony и Zend . Они оба превосходны. Также взгляните на пару ORM, таких как Doctrine и Propel .

Также см. Статью в Википедии MVC .

0 голосов
/ 05 мая 2011

Очевидно, что вы не хотите создавать новое соединение каждый раз, когда создаете новую модель, поэтому вы хотите инициализировать объект PDO отдельно. (Однажды я наткнулся на внутреннюю инфраструктуру MVC, где Model расширил свой класс базы данных! Как вы можете себе представить, это было не быстро ...)

Может быть, это то, что вы хотите?

abstract class Model
{
    protected $db = NULL;

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

Пример использования

class Article extends Model
{
    public $props = array ();

    private function getPage ($id)
    {
        $q = $this -> db -> prepare ('select * from pages where pageId = :pageId');
        $q -> execute (array ('pageId' => $id));
        $ret = $res -> fetchAll ();
        return ($this -> props = $ret);
    }
}

$db = new PDO ('dsn goes here');

$page = new Article ($db);
$page -> getPage (1234);

Как найти примеры? Попробуйте поискать "php mvc" и посмотреть статьи, которые появляются

0 голосов
/ 05 мая 2011

Вы могли бы взглянуть на подход модели отображения данных, на который ссылается Zend Framework Model manual

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

...