Проектирование общего интерфейса базы данных в 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']
Приветствия.
Ответ 3
Вы заглянули в http://www.doctrine-project.org/ или другие рамки php orm (zend_db приходит на ум)?
Ответ 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 как можно больше и перетащить его через несколько основных функций, вы можете гарантировать, что все правильно экранировано довольно легко.
Если вы хотите посмотреть мой класс базы данных, сообщите мне.