MySQL + PHP: реализация хорошей модели - PullRequest
4 голосов
/ 29 июля 2010

В проекте MVC-Model View Controller вы реализуете модель, чтобы работать отдельно и содержать логику ведения бизнеса, извлекая информацию из базы данных.

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

  1. Что делать, если мне нужно отсортировать поля?
  2. Что если мне нужно выбрать по определенному имени пользователя / идентификатору?
  3. Что если мне нужно сгруппировать по определенному полю?
  4. Насколько сильно это повлияет на производительность, если я выберу * только в том случае, если вызывающей функции может понадобиться какая-либо информация из раскрывающегося списка?

Мой API / Модель становится чрезвычайно раздутым, имея отдельные функции и запросы (просто слегка настроенные / измененные) для каждой функции

Например

$cart->getShoppingCart()
$cart->getShoppingCartSortByTitle()
$cart->getShoppingCartGroupByItemType()

Я чувствую, что это делает модель чрезвычайно раздутой и очень связанной, создавая много дублированного кода. Может быть, лучше иметь такую ​​модель;

Лучшая идея

$cart->getItems('title, price')->order_by('title');

Где 'title, price' - это поля mySQL, которые вы можете выбрать, очевидно проверяемые функцией getItems(). Таким образом, можно не только возвращать определенные поля.

  1. Как мне этого добиться?
  2. Это действительно хорошая модель?
  3. Есть что-нибудь, что вы, ребята, могли бы предложить?

Ответы [ 6 ]

1 голос
/ 01 августа 2010

Использовать объектно-реляционное отображение (ORM) ...

Попробуйте проект Doctrine ORM .

Другое решение - CodeIgniter , оноимеет лучшую библиотеку активных записей там.Очень полезно.

Если вы все еще решили написать собственный класс, тогда используйте PHP5 метод цепочки .Синтаксис был бы красивее ...

0 голосов
/ 02 августа 2010
0 голосов
/ 01 августа 2010

Прежде всего вы должны принять во внимание:

  • Хорошей общей модели не существует. Каждому проекту нужна своя модель.
  • легко читаемый, управляемый код
  • не повторяйте один и тот же код (или запросы), поэтому, если у вас есть функция для определенной задачи и вы хотите, чтобы она была упорядочена другим способом, измените саму функцию и не клонируйте ее
  • Использование сложных структур данных, таких как массивы или объекты, для отправки данных в функцию, поэтому вам не нужно всегда изменять параметры, необходимые для функции
  • использование ресурсов. Чем больше вы хотите, чтобы это было вокруг, общее решение, тем больше ресурсов он будет использовать.

Насколько сильно будет зависеть производительность, если я выберу * только в том случае, если вызывающей функции может понадобиться какая-либо информация из списка?

Это зависит от загрузки вашего сайта. Большую часть времени (если вы не тянете большие большие двоичные объекты и текст) * все в порядке, но когда ресурсов мало, вы должны указать столбцы. Таким образом, вы можете сэкономить время ввода-вывода.


Я чувствую, что это делает модель чрезвычайно раздутой и очень связанной, создавая много дублированного кода. Может быть лучше иметь такую ​​модель;

Может быть попробовать это:

Прежде всего, для сложных запросов я использую этот класс, который я создал давно для MySQL. Это очень помогает.

class sqlAssembler
{
    private $data = array();
    var $S = array();
    var $F = array();
    var $W = array();
    var $G = array();
    var $H = array();
    var $O = array();
    var $L = array();

    //Clause abbreviations
    var $clauselist = array
    (
    'S' => 'SELECT',
    'F' => 'FROM',
    'W' => 'WHERE',
    'G' => 'GROUP BY',
    'H' => 'HAVING',
    'O' => 'ORDER BY',
    'L' => 'LIMIT'
    );

    //Default clause separators
    var $clausesep = array
    (
    'S' => ',',
    'F' => ',',
    'W' => ' AND ',
    'G' => ',',
    'H' => ' AND ',
    'O' => ',',
    'L' => ''
    );

    function gen()
    {
        $tmp = '';

        foreach ( $this->clauselist as $area => $clause )
        {
            if ( count($this->{$area}) )
            {
                $tmp .= ($clause != 'S' ? ' ' : '') . $clause . ' ';
                for ($i=0; $i < count($this->{$area}); $i++) 
                {
                    //echo $area = (string)$area;
                    $tmp .= $this->{$area}[$i];
                } //for
            } //if
        } //foreach

        return $tmp;
    } //function


    function genSection($area, $showsection = 0)
    {
        $tmp = '';
        if ( count($this->{$area}) )
        {
            for ($i=0; $i < count($this->{$area}); $i++) 
            {
                $tmp .= $this->{$area}[$i];
            } //for
        } //if

        return $tmp;
    } //function

    function clear()
    {
        foreach ($this as $area => $v)
        {
            //We only care about uppercase variables... do not declare any else variable with ALL UPPERCASE since it will be purged
            if (ctype_upper($area))
            {
                if ($area == 'L')
                    $this->$area = '';
                else
                    $this->$area = array();
            } //if
        } //foreach
    } //function

    public function add($area, $str, $criteria = 1, $sep = '#')
    {
        if ($criteria)
        {
            if ($sep == '#')
                $sep = $this->clausesep[$area];

            //Postgres' OFFSET should be set like: $str = '25 OFFSET 0'
            //Not very neat I know, but fuck it
            if ($area == 'L')
            {
                $this->{$area} = array();   
            } //if

            //$ref = $this->$area;
            $this->{$area}[] = (count($this->$area) ? $sep : '').$str;

            return count($this->$area)-1;
        } //if
    } //function

    public function del($area,$index)
    {
        if ( isset($this->{$area}[$index]) )
            unset($this->{$area}[$index]);
        else
            trigger_error("Index nr. {$index} not found in {$area}!",E_USER_ERROR);
    } //function

//-*-* MAGIC CHAIN FUNCTIONS 

    public function S($str,$criteria = 1,$sep = '#')
    {
        $this->add(__FUNCTION__,$str,$criteria,$sep);
        return $this;
    } //function

    public function F($str,$criteria = 1,$sep = '#')
    {
        $this->add(__FUNCTION__,$str,$criteria,$sep);
        return $this;
    } //function

    public function W($str,$criteria = 1,$sep = '#')
    {
        $this->add(__FUNCTION__,$str,$criteria,$sep);
        return $this;
    } //function

    public function G($str,$criteria = 1,$sep = '#')
    {
        $this->add(__FUNCTION__,$str,$criteria,$sep);
        return $this;
    } //function

    public function H($str,$criteria = 1,$sep = '#')
    {
        $this->add(__FUNCTION__,$str,$criteria,$sep);
        return $this;
    } //function

    public function O($str,$criteria = 1,$sep = '#')
    {
        $this->add(__FUNCTION__,$str,$criteria,$sep);
        return $this;
    } //function

    public function L($str,$criteria = 1,$sep = '#')
    {
        $this->add(__FUNCTION__,$str,$criteria,$sep);
        return $this;
    } //function
} //_sql

Может быть попробовать это:

function getShoppingCart($d)
{
    $xx = new sqlAssembler();

    $xx->S('*')->
    F('items')->
    //Notice, that we specified a criteria... if $d['id_item'] exists it will be joined to the WHERE clause, if not it will be left out
    W("(id_item > '{$d[id_item]}')",$d['id_item'])->
    //Same here
    O("dt DESC",$d['date'])
    $sql = echo $xx->gen();

    //id_item = 11, date = 2009-11-12
    //$sql = "SELECT * FROM items WHERE (id_item > '11') ORDER BY dt DESC";

    //id_item = null, date = null
    //$sql = "SELECT * FROM items";

    $data = sqlArray($sql);

    //... handle data
}
0 голосов
/ 30 июля 2010

Прежде всего, убедитесь, что все эти «вопросы», которые вы задаете себе, касаются функций, которые действительно нужны вашему приложению прямо сейчас. Одна из самых больших проблем, с которыми я столкнулся при разработке новых проектов, - это спекулятивный дизайн. Добавляйте только то, что вам нужно. Напишите юнит-тесты для того, что вы добавляете. Когда вы дойдете до точки, где вам нужны дополнительные функции, при необходимости измените рефакторинг на лучший дизайн.

Если вам нужны все функции заранее, я все же рекомендую подход рефакторинга. Реализуйте несколько функций, которые вы видите похожими или способствующими «раздутию». Когда вы закончите, сделайте шаг назад и посмотрите, сможете ли вы реорганизовать что-то более элегантное или более равномерное распределение ответственности между различными объектами и / или методами. Тогда продолжай. Здесь вам очень помогут различные книги по шаблонам и рефакторингу.

0 голосов
/ 29 июля 2010

«Лучшая идея» - определенно лучшая идея. Вы могли бы рассмотреть вопрос о том, как Django реализует этот , потому что этот подход используется там. Django написан на Python, что немного упрощает некоторые вещи, но вы должны быть в состоянии использовать и те же концепции в PHP (только с чуть меньшей аккуратностью). В целом, хотя создание запроса создает объект запроса, который имеет методы, такие как order_by. Применение этих методов изменит состояние запроса, и только когда запрос фактически выполняется, ему нужно сгенерировать SQL и выполнить его в базе данных.

Если вы придерживаетесь первого, вы можете рассмотреть возможность использования «магических методов» с динамическими именами, как это делают многие существующие фреймворки. Например,

getShoppingCart_groupby
getShoppingCart_orderby

У вас будет один метод «catch-all» с динамическим списком аргументов, который читает имя вызываемой функции и выполняет требуемое поведение, если оно допустимо (и выдает стандартную ошибку «method not found», если это не). По сути, это то же самое, что вы делаете сейчас, но это будет приводить в порядок код и не загромождать вашу модель. Для этого вам понадобится PHP5, и вы ищете магический метод __call .

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

0 голосов
/ 29 июля 2010

Управление базой данных Code Igniter позволяет выполнять такой запрос к базе данных, ограничивая копируемый / вставляемый код.
См. Модуль активных записей в ихдокументация

И я думаю, что их модель неплохая.

...