База данных
База данных
Подключение к ядру базы данных
Fat-Free предназначен для того, чтобы сделать работу по взаимодействию с базами данных SQL легкой. Если вы не из тех, кто углубляется в детали SQL, но больше склоняетесь к объектно-ориентированной обработке данных, вы можете перейти непосредственно к следующему разделу этого руководства. Однако, если вам нужно выполнить некоторые сложные задачи по обработке данных и оптимизации производительности базы данных, SQL-это правильный путь.
Установление связи с SQL-движком, таким как MySQL, SQLite, PostgreSQL, SQL Server, Sybase и Oracle, осуществляется с помощью знакомой $f3->set()
команды. Подключение к базе данных SQLite было бы:
Теперь вы можете работать с объектом базы данных из любого места вашего приложения $f3->get('DB')->exec('…');
.
Еще один пример, на этот раз с MySQL:
Вы также можете посмотреть видео, которое проходит через использование MySQL в Fat-Free Framework.
Запрос к базе данных
ОК. Это было легко, не так ли? Примерно так же вы поступили бы и в обычном PHP. Вам просто нужно знать формат DSN базы данных, к которой вы подключаетесь. Смотрите раздел PDO руководства по PHP.
Давайте продолжим наш PHP код:
Хм, что здесь происходит? Разве мы не должны создавать такие вещи, как PDO, операторы, курсоры и т. д.? Ответ прост: вы не должны этого делать. F3 упрощает все, взяв на себя всю тяжелую работу в бэкэнде.
На этот раз мы создаем такой HTML шаблон abc.htm
который имеет как минимум следующее:
В большинстве случаев набора команд SQL должно быть достаточно для генерации готового к работе в Интернете результата, чтобы вы могли напрямую использовать переменную result
массива в своем шаблоне. Как бы то ни было, Fat-Free не помешает вам проникнуть в его внутренние части SQL-обработчика. На самом деле DB\SQL
класс F3 происходит непосредственно от PDO
класса PHP, поэтому у вас все еще есть доступ к базовым компонентам PDO и примитивам, участвующим в каждом процессе, если вам нужен какой-то низкоуровневый контроль.
Операции
Вот еще один пример. Вместо одного оператора, предоставленного в качестве аргумента $db->exec()
команде, вы также можете передать массив SQL-операторов:
F3 достаточно умен, чтобы знать, что если вы передаете массив инструкций SQL, это указывает на пакетную транзакцию SQL. Вам не нужно беспокоиться об откатах и фиксациях SQL, потому что фреймворк автоматически вернется к исходному состоянию базы данных, если во время транзакции возникнет какая-либо ошибка. В случае успеха F3 фиксирует все изменения, внесенные в базу данных.
Вы также можете запускать и завершать транзакцию программно:
Откат произойдет, если какой-либо из операторов столкнется с ошибкой.
Чтобы получить список всех выданных инструкций базы данных:
параметризованный запрос
Передача строковых аргументов в SQL-операторы чревата опасностью.
Если POST
переменная userID
не проходит через какой-либо процесс очистки данных, злоумышленник может передать следующую строку и необратимо повредить вашу базу данных:
К счастью, параметризованные запросы помогают снизить эти риски:
Если F3 обнаруживает, что значение параметра запроса/токена является строкой, базовый уровень доступа к данным экранирует эту строку и добавляет кавычки по мере необходимости.
Наш пример в предыдущем разделе будет намного безопаснее от SQL-инъекции, если он будет написан таким образом:
Вы также можете посмотреть видео, посвященное SQL-инъекции и безопасности базы данных.
Грубость (но с большим количеством стиля)
F3 упакован с простыми в использовании объектно-реляционными сопоставителями (ORM), которые находятся между вашим приложением и вашими данными, что делает его намного проще и быстрее для вас писать программы, которые обрабатывают общие операции с данными, такие как создание, извлечение, обновление и удаление информации (CRUD) из баз данных SQL и NoSQL. Картографы данных выполняют большую часть работы, сопоставляя взаимодействия объектов PHP с соответствующими бэкенд-запросами.
Предположим, у вас есть существующая база данных MySQL, содержащая таблицу пользователей вашего приложения. (SQLite, PostgreSQL, SQL Server, Sybase подойдут точно так же.) Он был бы создан с помощью следующей команды SQL:
Примечание: MongoDB-это движок базы данных NoSQL, который по своей сути не содержит схем. F3 имеет свою собственную быструю и легкую реализацию NoSQL под названием Jig, которая использует файлы, сериализованные на PHP или закодированные в JSON. Эти уровни абстракции не требуют жестких структур данных. Поля могут варьироваться от одной записи к другой. Они также могут быть определены или отброшены на лету.
Теперь вернемся к SQL. Во-первых, мы устанавливаем связь с нашей базой данных.
Чтобы извлечь запись из нашей таблицы:
В первой строке создается экземпляр объекта Data mapper, который взаимодействует с users
таблицей в нашей базе данных. За сценой F3 извлекает структуру users
таблицы и определяет, какие поля определяются как первичные ключи. На данный момент объект mapper еще не содержит никаких данных (он называется "сухое состояние"), а $user
var - это в основном не более чем структурированный объект, но он содержит методы, необходимые для выполнения основных операций CRUD, плюс некоторые дополнительные функции, как вы увидите позже. Теперь, чтобы извлечь запись из нашей users
таблицы, например, с полемuserID
, содержащим строковое значение tarzan
, мы используем load()
метод. Этот процесс называется "автогидратацией" объекта Data mapper.
Легко, не правда ли? F3 понимает, что таблица SQL уже имеет структурное определение, существующее в самом ядре СУБД. В отличие от других фреймворков, F3 не требует никаких дополнительных объявлений классов (если только вы не хотите расширить картографы данных, чтобы они соответствовали сложным объектам), никаких избыточных сопоставлений свойств PHP-массива/объекта в поле (дублирование усилий), никаких генераторов кода (которые требуют регенерации кода при изменении структуры базы данных), никаких глупых XML/YAML-файлов для настройки ваших моделей, никаких лишних команд просто для извлечения одной записи. С помощью F3 простое изменение размера a varchar
поле в вашей таблице MySQL не требует ни одного изменения в коде вашего приложения. В соответствии с MVC и "разделением проблем" администратор базы данных имеет такой же контроль над данными и структурами, как дизайнер шаблонов над шаблонами HTML/XML.
Если вы предпочитаете работать с базами данных NoSQL, то сходство в синтаксисе запросов поверхностно. В случае картографа данных MongoDB эквивалентным кодом будет:
В jig синтаксис аналогичен шаблонному движку F3:
Интеллектуальный SQL ORM
Фреймворк автоматически сопоставляет поле visits
в нашей таблице со свойством Data mapper во время создания экземпляра объекта, $user=new DB\SQL\Mapper($db,'users');
т. е. Как только объект будет создан, $user->password
он $user->userID
будет сопоставлен с полями password
и userID
в нашей таблице соответственно.
Вы не можете добавить или удалить сопоставленное поле или изменить структуру таблицы с помощью ORM. Вы должны сделать это в MySQL или любом другом движке базы данных, который вы используете. После внесения изменений в компонент database engine Fat-Free автоматически синхронизирует новую структуру таблицы с объектом Data mapper при запуске приложения.
F3 выводит структуру картографа данных непосредственно из схемы базы данных. Никаких догадок. Он понимает различия между движками баз данных MySQL, SQLite, MSSQL, Sybase и PostgreSQL.Обратите внимание: идентификаторы SQL не должны использовать зарезервированные слова и должны быть ограничены буквенно-цифровыми символами A-Z0-9
и символом подчеркивания (_
). Имена столбцов, содержащие пробелы (или специальные символы) и заключенные в кавычки в определении данных, несовместимы с ORM. Они не могут быть представлены должным образом как свойства объекта PHP.
Допустим, мы хотим увеличить количество посещений пользователя и обновить соответствующую запись в нашей users
таблице, мы можем добавить следующий код:
Если мы хотим вставить запись, мы следуем этому процессу:
Мы все еще используем тот же save()
метод. Но как F3 узнает, когда запись должна быть вставлена или обновлена? В тот момент, когда объект картографа данных автоматически гидратируется извлечением записи, фреймворк отслеживает первичные ключи записи (или _id
, в случае MongoDB и Jig) - так что он знает, какая запись должна быть обновлена или удалена-даже при изменении значений первичных ключей. Программный гидратированный картограф данных, значения которого не были извлечены из базы данных, но заполнены приложением, не будет иметь памяти предыдущих значений в своих первичных ключах. То же самое относится к MongoDB и Jig, но с использованием объекта _id
в качестве ссылки. Итак, когда мы создали экземпляр $user
объект выше и заполнил его свойства значениями из нашей программы - вообще не извлекая запись из пользовательской таблицы, F3 знает, что он должен вставить эту запись.
Объект mapper не будет пустым после a save()
. Если вы хотите добавить новую запись в свою базу данных, вы должны сначала очистить картограф с помощью этого reset
метода:
Вызов save()
во второй раз без вызова reset()
просто обновит запись, на которую в данный момент указывает картограф.
Предостережение для таблиц SQL
Хотя проблема наличия первичных ключей во всех таблицах базы данных является спорной, F3 не мешает вам создать объект сопоставления данных, который взаимодействует с таблицей, не содержащей первичных ключей. Единственный недостаток: вы не можете удалить или обновить сопоставленную запись, потому что F3 абсолютно не может определить, на какую запись вы ссылаетесь, плюс тот факт, что позиционные ссылки ненадежны. Идентификаторы строк не переносимы между различными движками SQL и не могут быть возвращены драйвером базы данных PHP.
Чтобы удалить сопоставленную запись из нашей таблицы, вызовите этот erase()
метод на автогидратированном картографе данных. Например:
Синтаксис запроса джига был бы немного похож:
И эквивалент MongoDB был бы таким:
Состояние Данных Картографа
Чтобы узнать, был ли наш картограф данных загружен с допустимой записью данных или нет, используйте этот dry
метод:
За гранью грубости
Мы разобрались с обработчиками грязи. Есть некоторые дополнительные методы, которые вы можете найти полезными:
Обратите внимание, что мы также можем использовать обезжиренные переменные в качестве контейнеров для объектов mapper. copyFrom()
Метод увлажняет объект mapper элементами из переменной фреймворка массива, ключи массива которого должны иметь имена, идентичные свойствам объекта mapper, которые, в свою очередь, соответствуют именам полей записи. Таким образом, при отправке веб-формы (при условии, что атрибут HTML name установлен в userID
значение) содержимое этого поля ввода передается$_POST['userID']
, дублируется F3 в его POST.userID
переменной и сохраняется в сопоставленном поле $user->userID
в базе данных. Процесс становится очень простым, если все они имеют одинаковые имена элементов. Согласованность в ключах массива, т. е. именах шаблонных маркеров, именах переменных фреймворка и именах полей является ключевой :)Опасность: по умолчанию copyfrom
используется весь предоставленный массив. Это может привести к утечке данных безопасности, если пользователь разместит больше полей, чем вы ожидаете. Используйте 2-й параметр для настройки функции обратного вызова фильтра, чтобы избавиться от нежелательных полей для копирования.
С другой стороны, если мы хотим получить запись и скопировать значения полей в переменную фреймворка для последующего использования, например для рендеринга шаблона:
Затем мы можем назначить {{ @POST.userID }} для атрибута value того же поля ввода. Подводя итог, можно сказать, что поле ввода HTML будет выглядеть следующим образом:
Методыsave()
,update()
, copyFrom()
Data mapper и параметризованные варианты load()
and erase()
безопасны от SQL-инъекции.
Навигация и разбиение на страницы
По умолчанию load()
метод сопоставления данных извлекает только первую запись, соответствующую заданным критериям. Если у вас есть несколько записей, удовлетворяющих тому же условию, что и первая загруженная запись, вы можете использовать этот skip()
метод для навигации:
Вы можете использовать $user->next()
в качестве замены$user->skip()
, и $user->prev()
если вы думаете, что это дает больше смысла$user->skip(-1)
.
Используйте этот dry()
метод, чтобы проверить, не вышли ли вы за пределы результирующего набора. dry()
вернет TRUE, если вы попробуете skip(-1)
на первой записи. Он также вернет TRUE, если вы skip(1)
на последней записи, которая соответствует критериям извлечения.
load()
Метод принимает второй аргумент: массив опций, содержащий пары ключ-значение, такие как:
Если вы используете MySQL, запрос преобразуется в:
Это один из способов представления данных в виде небольших фрагментов. Вот еще один способ разбиения результатов на страницы:
В приведенном выше сценарии F3 будет извлекать записи, соответствующие этим критериям 'visits>3'
. Затем он ограничит результаты 5 записями (на страницу), начиная со смещения страницы 2 (на основе 0). Фреймворк вернет массив состоящий из следующих элементов:
Фактическая возвращаемая позиция подмножества будет равна нулю, если первый аргумент paginate()
является отрицательным числом или превышает число найденных подмножеств.
Виртуальные Поля
Бывают случаи, когда вам нужно получить вычисленное значение поля или значение перекрестной ссылки из другой таблицы. Введите виртуальные поля. SQL mini-ORM позволяет работать с данными, полученными из существующих полей.
Предположим, что у нас есть следующая таблица, определенная как:
totalprice
Поля не существует, поэтому мы можем сказать фреймворку, чтобы он запросил у компонента database engine арифметическое произведение этих двух полей:
Приведенный выше фрагмент кода определяет вызываемое виртуальное полеtotalprice
, которое вычисляется путем умножения unitprice
на число quantity
. SQL mapper сохраняет это правило / формулу, поэтому, когда приходит время извлекать запись из базы данных, мы можем использовать виртуальное поле как обычное отображенное поле.
Вы можете иметь более сложные виртуальные поля:
На этот раз фреймворк извлекает продукт с наибольшим количеством (обратите load()
внимание, что метод не определяет никаких критериев, поэтому все записи в таблице будут обработаны). Конечно, виртуальное поле mostNumber
все равно даст вам правильную цифру, если вы хотите ограничить выражение определенной группой записей, соответствующих заданным критериям.
Вы также можете получить значение из другой таблицы:
Каждый раз, когда вы загружаете запись из таблицы products, ORM перекрестно ссылается на supplerID
in the products
table с supplierID
in the suppliers
table.
Чтобы уничтожить виртуальное поле, используйте unset($item->totalPrice);
. isset($item->totalPrice)
Выражение возвращает TRUE, если totalPrice
виртуальное поле было определено, или FALSE, если нет.
Помните, что виртуальное поле должно быть определено до получения данных. ORM не выполняет ни собственно вычисление, ни вывод результатов из другой таблицы. Именно компонент database engine выполняет всю тяжелую работу.
Ищите и вы найдете
Если у вас нет необходимости в навигации по записям, вы можете получить всю партию записей одним выстрелом:
Синтаксис запросов jig mapper имеет небольшое сходство:
Эквивалентный код с использованием картографа MongoDB:
find()
Метод ищет в users
таблице записи, соответствующие критериям, сортирует результат по userID
ним и возвращает результат в виде массива объектов mapper. find('visits>3')
отличается от load('visits>3')
него . Последнее относится к текущему $user
объекту. find()
не оказывает на него никакого влияния skip()
.Важно:
Объявление пустого условия, NULL или строки нулевой длины в качестве первого аргумента find()
or load()
приведет к извлечению всех записей. Убедитесь, что вы знаете, что делаете - вы можете превысить "memory_limit" PHP на больших таблицах или коллекциях
Этот find()
метод имеет следующий синтаксис:
функция find() возвращает массив объектов. Каждый объект является сопоставителем записи, соответствующей заданным критериям.:
Если вам нужно преобразовать объект mapper в ассоциативный массив, используйте этот cast()
метод:
Чтобы получить количество записей в таблице, соответствующих определенному условию, используйте этот count()
метод.
Существует также select()
метод, который аналогиченfind()
, но обеспечивает более мелкозернистый контроль над возвращаемыми полями. Он имеет SQL-подобный синтаксис:
Как find()
и метод, select()
он не изменяет содержимое объекта mapper. Он служит только в качестве удобного метода для запроса сопоставленной таблицы. Возвращаемое значение обоих методов представляет собой массив объектов mapper. Использование dry()
для определения того, была ли запись найдена одним из этих методов, неуместно. Если ни одна запись не соответствует критерию find()
илиselect()
, возвращаемое значение является пустым массивом.Имейте в виду:
load()
гидратирует текущий объект mapper, findone
возвращает новый гидратированный объект mapper и find
возвращает массив гидратированных объектов mapper.Опасность: массив $options for не использует параметризованные значения полей и не очищается от входных данных пользователя. Если вы просто помещаете значения GET или POST в группу, ордер, лимит или смещение, не проверяя их заранее, вы открываете проблему безопасности, и могут произойти плохие вещи.
Профилирование
Если вы когда-нибудь захотите выяснить, какие операторы SQL, выпущенные непосредственно вашим приложением (или косвенно через объекты mapper), вызывают узкие места производительности, вы можете сделать это с помощью простого:
F3 отслеживает все команды, выданные базовому драйверу базы данных SQL, а также время, необходимое для завершения каждой инструкции - именно та информация, которая необходима для настройки производительности приложения.
Иногда этого просто недостаточно
В большинстве случаев вы можете жить с комфортом, предоставляемым методами картографирования данных, которые мы обсуждали до сих пор. Если вам нужен фреймворк для выполнения какой-то тяжелой работы, вы можете расширить SQL mapper, объявив свои собственные классы с помощью пользовательских методов, но вы не можете избежать того, чтобы ваши руки были жирными на некоторых хардкорных SQL:
Расширение карт данных таким образом-это простой способ построить модели, связанные с БД вашего приложения.
плюсы и минусы
Если вы хорошо разбираетесь в SQL, вы, вероятно, скажете: все в ORM можно обрабатывать с помощью SQL-запросов старой школы. Действительно. Мы можем обойтись без дополнительных прослушивателей событий, используя триггеры базы данных и хранимые процедуры. Мы можем выполнять реляционные запросы с Объединенными таблицами. ОРМ - это просто ненужные накладные расходы. Но суть в том, что картографы данных дают вам дополнительную функциональность использования объектов для представления сущностей базы данных. Как разработчик, вы можете писать код быстрее и продуктивнее. Полученная программа будет чище, если не короче. Но вам придется взвесить преимущества против компромисса в скорости - особенно при работе с большими и сложными хранилищами данных. Помните, что все ОРМЫ - какими бы тонкими они ни были - всегда будут просто еще одним абстрактным слоем. Они все еще должны передать работу базовым SQL-движкам.
По замыслу, ORMs F3 не предоставляют методов для непосредственного соединения объектов друг с другом, то есть SQL - соединений, потому что это открывает банку червей. Это делает ваше приложение более сложным, чем оно должно быть, и есть тенденция объектов через нетерпеливые или ленивые методы выборки быть заблокированными и даже несинхронизированными из-за наследования объектов и полиморфизма (несоответствия импеданса) с объектами базы данных, с которыми они сопоставлены. Есть косвенные способы сделать это в SQL mapper, используя виртуальные поля - но вам придется делать это программно и на свой страх и риск.
Если вы испытываете искушение применить" чистые " концепции ООП в своем приложении для представления всех ваших данных (потому что "все является объектом"), имейте в виду, что данные почти всегда живут дольше, чем приложение. Ваша программа может устареть задолго до того, как данные потеряют свою ценность. Не добавляйте еще один уровень сложности в свою программу, используя переплетенные объекты и классы, которые слишком сильно отклоняются от схемы и физической структуры данных.
Прежде чем объединять несколько объектов в приложении для управления базовыми таблицами в базе данных, подумайте вот о чем: создание представлений для представления связей и триггеров для определения поведения объектов в ядре СУБД более эффективно. Движки реляционных баз данных предназначены для обработки представлений, Объединенных таблиц и триггеров. Это не тупые хранилища данных. Таблицы, объединенные в представление, будут отображаться как одна таблица, и Fat-Free может автоматически отображать представление так же, как и обычную таблицу. Репликация соединений как реляционных объектов в PHP происходит медленнее по сравнению с машинным кодом ядра СУБД, реляционной алгеброй и логикой оптимизации. Кроме того, многократное объединение таблиц в нашем приложении является верным признаком того, что дизайн базы данных нуждается в аудите, а представления считаются неотъемлемой частью поиска данных. Если таблица часто ссылается на данные из другой таблицы, подумайте о нормализации структуры или создании представления. Затем создайте объект mapper для автоматического отображения этого представления. Это быстрее и требует меньше усилий.
Рассмотрим это представление SQL, созданное внутри вашего ядра СУБД:
Код вашего приложения становится простым, потому что ему не нужно поддерживать два объекта mapper (один для таблицы проектов, а другой для пользователей) только для извлечения данных из двух соединенных таблиц:
Совет: используйте инструменты так, как они предназначены. Fat-Free уже имеет простой в использовании помощник SQL. Используйте его, если вам нужен больший молоток :) попробуйте найти баланс между удобством и производительностью. SQL всегда будет вашим резервным вариантом, если вы работаете со сложными и устаревшими структурами данных.
Last updated