Проектирование общего интерфейса базы данных в PHP

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

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

Я уже думал об использовании шаблона MVC, но выяснил, что это будет немного излишним для моего довольно небольшого проекта (ов).

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

  • Является ли (по-прежнему) хорошей идеей смешать в SQL прямо в PHP-код? (Был бы "старый путь": mysql_query( 'SELECT firstname, lastname(.....))?

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

    SELECT firstname, lastname FROM customers WHERE id=X
    

Будет ли MySQL "помощником" функционировать как

$this->db->customers->getBy( 'id', $x );

- хорошая идея?

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

  • Является ли модель "Модель" у MVC моей единственной реальной возможностью решить эту проблему?

  • Что вы в настоящее время используете для решения проблем, показанных выше?

Ответ 1

Если вам нужна скорость, используйте необработанные запросы (но вы действительно должны использовать PDO с подготовленные запросы).

Если вы хотите что-то большее ООП, вы можете, если вы предлагаете это, спроектировать это с помощью помощников.

Однажды я разработал нечто похожее, которое имело следующую концепцию:

  • классы подключения/обработчика DB (обработка множественных подключений к различным базам данных и различным серверам, таким как MySQL, Oracle и т.д.);
  • Класс за действие (т.е. SELECT, DELETE и т.д.);
  • Классы фильтров (например, RangeFilter);

Код выглядел примерно так:

$select = new Select('field1', 'field2', );
$result = $select->from('myTable')
                 ->addFilter(SQLFilter::RangeFilter, 'field2')
                 ->match(array(1, 3, 5))
                 ->unmatch(array(15, 34))
                 ->fetchAll();

Это простой пример того, как вы можете его создать.

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

Возможно, это долгая и тяжелая работа, но на самом деле вам не потребуется столько времени, чтобы сделать все эти функции (≈1 месяц).

Ответ 2

Я считаю, что вы просто хотите получить доступ к своей БД из своего модуля. Я бы не использовал mysql_query непосредственно из кода. Скорее, для простой модели с абстрактным доступом к БД было бы легко и прямолинейно.

Например, у вас может быть такой файл, как models/Customers.php, с помощью этого кода:

<?php

class Customers {

    public function getById($id) {
        $sql = "SELECT first_name, last_name FROM customers WHERE id='$id'";
        $res = $DB::getRow($sql);
        return ($res);
    }
}

Я предполагаю, что какой-то помощник DB уже создан и доступен как $DB. Здесь является простой, который использует PDO.

Теперь вы должны включить это в свой модуль и использовать следующий способ:

<?php

include_once "models/Customers.php";

$customers = new Customers();
$theCustomer = $customers->getById(intval($_REQUEST['cust_id']));


echo "Hello " . $theCustomer['first_name']

Приветствия.

Ответ 4

Три подсказки:

  • Используйте хранимые процедуры (чтобы вы могли отделить php от db)
  • Использовать PDO/MySQLi для подготовленных операторов CALL NEWS_LIST(?, ?)
  • Используйте статический класс для вашей БД. Позволяет вам получить доступ к нему в любом модуле.

Ответ 5

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

Вы должны использовать PDO, который уже обеспечивает большую мощность, и если этого недостаточно, вы можете расширить его (возможно, с вашими собственными функциями, такими как проверка на наличие хитов в Memcached/APC перед фактическим запросом базы данных). Вы также можете расширить класс для реализации своих собственных SQL-функций, таких как:

function getUser($user_id) {
    return $this->query("SELECT * FROM users WHERE id = " . (int) $user_id);
}

Конечно, из модели вы все равно можете отправить:

$this->db->query("SELECT * FROM users WHERE id = " . (int) $user_id);

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

Шаблон MVC отлично впишется в это, потому что вы можете использовать базу данных только как драйвер, и ваша модель может затем преобразовать данные в то, что вам нужно. Нетрудно создать простую структуру MVC, и это принесет вам преимущества позже.

Ответ 6

Ты говоришь как я. Вы видели http://github.com/Xeoncross/micromvc и один файл ORM в http://github.com/Xeoncross/database? Копайте мой код, и я думаю, вы найдете то, что ищете.

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

Оба хороши.

Ответ 7

Не то, чтобы я знал окончательный ответ (и не думаю, что он существует), но я думал, что могу просто поделиться тем, что у меня есть. Я использую свою собственную инфраструктуру db, легкую (~ 1000 строк в настоящее время) и простую в использовании. Моя главная цель заключалась в том, чтобы упростить использование sql, а не "скрывать" его от программиста (мне:). Некоторые примеры:

 // row() is 'query' + 'fetch' in one
 $user = $db->row("select * from users where id=25");

 // the same, injection safe
 $user = $db->row("select * from users where id=?", $_GET['id']);

 // ? placeholders are smart
 $someUsers = $db->rows("select * from users where id IN(?)", array(1, 2, 10));

 // ...and even smarter
 $data = array('name' => 'Joe', 'age' => 50);
 $id = 222;
 $db->exec("update users set ?a where id=?", $data, $id);

 // 'advanced' fetch functions
 $topNames   = $db->vlist("select name from users order by name limit 10");
 $arrayOfIds = $db->nlist("select id from users where age > 90");

 // table() returns a Table Gateway
 $db->table('users')->delete('where id=?', 25);

 // yes, this is safe
 $db->table('users')->insert($_POST);

 // find() returns a Row Gateway object
 $db->table('users')
     ->find('where name=?', 'Joe')
     ->set('status', 'confirmed')
     ->save();

Ответ 8

Понять это: взаимодействие с базой данных является решаемой проблемой.

Итак, если вы действительно не хотите сделать это: а) для опыта или б), потому что вы OCD и хотите знать каждый символ кода, который вы будете использовать, я бы выбрал существующее решение.

И их много: PEAR:: MDB2, Zend:: Db, Creole, Doctrine, Propel, просто чтобы назвать несколько.

Ответ 9

Я только что вышел из пути "вспомогательных функций", и одна вещь, которая меня беспокоила, заключалась в том, что я продолжал добавлять функции в один файл, который рос и рос с одинаковыми или похожими или несуществующими функциями. Я думаю, что количество строк составило 600, и это, по моему мнению, способ для одного файла. Это не отвлекло меня от идеи, но я буду более организован для следующего похода. Вероятно, я разделил функции db на несколько файлов в соответствии с операцией db (select, insert и т.д.).

Поэтому мой совет - попробовать "вспомогательные функции" и быть таким же организованным, как вы можете.

Кроме того, я впервые использовал PDO и очень понравился. Его не так низкотехнологичны, как функции mysql() или как техника вздутия, как некоторые, о которых мы могли бы упомянуть, но не будем. Я снова буду использовать PDO.

Ответ 10

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

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

Отдельные страницы, такие как /users/show/1 (с использованием mod_rewrite), не используют raw SQL, а какой-то легкий ORM, который работает, как в следующем примере:

$user = $this->db
             ->users
             ->getBy( 'id', $id );

$this->db - это экземпляр класса абстракции базы данных с помощью метода __get( $tableName ). Доступ к свойству undefined users затем запускает его. Остальное объясняет сам; Запрос формируется из аргументов, переданных в getBy( ) (SQL escaping также обрабатывается им), и его результаты возвращаются как массив.

Я еще не закончил всю идею, но добавление нового пользователя в базу данных может выглядеть следующим образом:

$user = $this->db
             ->users
             ->new;
$user->id = 2;
$user->name = 'Joe';
$user->save( );

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

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

Ответ 11

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

global $db;
$db = new db;
$db->query ('... sql ...');

является избыточным, когда вы можете сделать

db::query ('... sql ...');

У меня есть набор функций SQL, которые я использую почти регулярно, чтобы уменьшить то, что раньше использовалось для многострочной экранированной партии SQL для одного вызова, например:

get_element ($table, $element, $value, $column='id');
get_row ($table, $value, $column='id');

Итак, если вы просто хотите получить имя из таблицы "клиенты", где id равен 4, вы:

$name = db::get_element ('customers', 'name', 4);

Существуют также сопутствующие функции query_element и query_row, где вы просто передаете ему строку SQL и возвращает один элемент/строку.

Наряду с функциями для вставки/обновления, например

$array = array (
    'name' => 'bob jones',
    'age' => 28
);
$insert_id = db::insert_array ('customers', $array);

$customer_details = db::get_row ('customers', $insert_id);

$customer_details['age'] = 30;

db:update_array ('customers, $customer_details);

Создает новую строку, вытаскивает детали, обновляет возраст, затем перезаписывает его в базу данных.

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

Если вам нужно использовать что-либо со сложными объединениями, то всегда лучше создать функцию для него getCustomerInfo(), но если вы просто хотите, чтобы общий поиск значения таблицы, создавая множество пользовательских методов, просто увеличивает вероятность ошибок в одном из них. Плюс экранирование данных очень важно - если вы можете использовать не-sql как можно больше и перетащить его через несколько основных функций, вы можете гарантировать, что все правильно экранировано довольно легко.

Если вы хотите посмотреть мой класс базы данных, сообщите мне.