Проектирование интерфейса базы данных в 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 моим единственным реальным вариантом решить это?
что вы в настоящее время используете для решения проблем, показанных выше?
11 ответов
Если вам нужна скорость, используйте необработанные запросы (но вы действительно должны использовать PDO с подготовленные запросы).
Если вы хотите что-то больше ООП, вы можете - как вы предлагаете-спроектировать это с помощью помощников.
однажды я разработал нечто подобное, которое имело следующую концепцию:
- DB connection/handler classes (обработка нескольких подключений к различным базам данных и различным серверам, таким как MySQL, Oracle и т. д.);
- класс за действие (т. е. Выберите, удалите и т. д.);
- классы фильтров (например. 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 месяц).
Я считаю, что вы просто хотите получить доступ к вашей БД из вашего модуля. Я бы избегал использования 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']
Ура.
вы заглянули в http://www.doctrine-project.org/ или другие фреймворки php orm (на ум приходит zend_db)?
три подсказки:
- используйте хранимые процедуры (чтобы вы могли отделить php от БД)
- используйте PDO/MySQLi для готовых операторов
CALL NEWS_LIST(?, ?)
- используйте статический класс для вашей БД. Позволяет получить к нему доступ в любом модуле.
Raw SQL по-прежнему является победителем для меня, мне нравится контролировать то, что я отправляю на сервер (для случаев, таких как использование индекса, сложные предложения соединения и т. д.), Поэтому я обычно держусь подальше от вспомогательных функций.
вы должны использовать 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, и это принесет вам преимущества позже.
ты говоришь как я. Вы видели http://github.com/Xeoncross/micromvc и один файл ORM в http://github.com/Xeoncross/database? Покопайтесь в моем коде, и я думаю, вы найдете то, что ищете.
решение состоит в том, чтобы использовать полную необработанную мощность некоторых запросов - все еще позволяя ORM и построителям запросов (например, codeigniter's AR) для других вещей.
оба хороши.
// 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();
поймите это: взаимодействие с базой данных является решенной проблемой.
Если вы действительно хотите сделать это a) для опыта или b) потому что вы ОКР и хотите знать каждый символ кода, который вы будете использовать, тогда я бы выбрал существующее решение.
и есть много: PEAR:: MDB2, Zend:: Db, креольский, доктрина, Propel, просто назвать несколько.
Я только что сошел с пути "вспомогательные функции", и единственное, что меня беспокоило, было то, что я продолжал добавлять функции в один файл, который рос и рос с идентичными или похожими или несуществующими функциями. Я думаю, что количество строк было на 600, и это, на мой взгляд, очень много для одного файла. Это не оттолкнуло меня от идеи, но я буду более организованным для следующего похода. Вероятно, я разделю функции БД на несколько файлов в соответствии с операцией БД (select, insert так далее...).
поэтому мой совет - попробовать "вспомогательные функции" и быть как можно более организованным.
кроме того, я использовал PDO в первый раз и очень понравилось. Его не так низко, как функции mysql () или как технология раздувания, как некоторые, мы могли бы упомянуть, но не будем. Я снова буду использовать PDO.
похоже, что есть много разных мнений по этой теме, и поскольку я еще не нашел действительно удовлетворительного ответа здесь, и щедрость почти закончилась, я просто напишу то, что я придумал в последние дни после некоторых проб и ошибок:
Я использую одноэлементный класс MySQL для обработки соединения и самых основных запросов, а также ошибок, которые могут возникнуть.
отдельные страницы типа /users/show/1
(используя mod_rewrite) не используйте raw SQL, но какой-то легкий ORM это работает как в следующем примере:
$user = $this->db
->users
->getBy( 'id', $id );
$this->db
является экземпляром класса абстракции базы данных с __get( $tableName )
метод. Доступ к неопределенному users
свойство затем запускает его. Остальное объясняет себя; запрос формируется из аргументов, переданных в getBy( )
(SQL escaping также обрабатывается им), и его результаты возвращаются в виде массива.
Я еще не закончил всю идею, но добавление нового пользователя в базу данных может выглядеть как следующий:
$user = $this->db
->users
->new;
$user->id = 2;
$user->name = 'Joe';
$user->save( );
как я уже сказал, концепция на самом деле не завершена и может иметь (огромные) недостатки в ней. Тем не менее, я думаю, что это может быть проще писать, более безопасно и проще поддерживать, чем простой MySQL. Некоторые другие хорошие стороны всей "вещи" заключаются в том, что она маленькая, поэтому довольно быстрая и довольно простая.
Я знаю, что это не может конкурировать с чрезвычайно мощными ORMs и фреймворками уже там, но я все еще создаю это по некоторым причинам упоминается в одном из моих комментариев выше.
если вы планируете сделать класс базы данных, это может быть идея, рассматривающая его как синглтон, позволяющий использовать его без объявления / создания его, как...
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');
Итак, если вы просто хотите получить имя из таблицы "клиенты", где идентификатор 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 на основе таблицы обычно является ошибкой I всегда находили-лучше просто обобщенно запросить базу данных, используя разумные функции.
Если вам нужно использовать что-либо со сложными соединениями, то всегда лучше создать функцию для нее getCustomerInfo (), например, но если вы просто хотите, чтобы общий поиск значений таблицы делал много пользовательских методов, просто увеличивает вероятность ошибок в одном из них. Плюс экранирование данных очень важно-если вы можете использовать не-sql как можно больше и направить его через несколько ядер функции вы можете убедиться, что все правильно избежал довольно легко.
если вы хотите посмотреть на мой пользовательский класс базы данных, дайте мне знать.