База данных

База данных

Подключение к ядру базы данных

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

Установление связи с SQL-движком, таким как MySQL, SQLite, PostgreSQL, SQL Server, Sybase и Oracle, осуществляется с помощью знакомой $f3->set()команды. Подключение к базе данных SQLite было бы:

$f3->set('DB', new DB\SQL('sqlite:/absolute/path/to/your/database.sqlite'));

Теперь вы можете работать с объектом базы данных из любого места вашего приложения $f3->get('DB')->exec('…');.

Еще один пример, на этот раз с MySQL:

$db=new DB\SQL(
    'mysql:host=localhost;port=3306;dbname=mysqldb',
    'admin',
    'p455w0rD'
);

Вы также можете посмотреть видео, которое проходит через использование MySQL в Fat-Free Framework.

Запрос к базе данных

ОК. Это было легко, не так ли? Примерно так же вы поступили бы и в обычном PHP. Вам просто нужно знать формат DSN базы данных, к которой вы подключаетесь. Смотрите раздел PDO руководства по PHP.

Давайте продолжим наш PHP код:

$f3->set('result',$db->exec('SELECT brandName FROM wherever'));
echo Template::instance()->render('abc.htm');

Хм, что здесь происходит? Разве мы не должны создавать такие вещи, как PDO, операторы, курсоры и т. д.? Ответ прост: вы не должны этого делать. F3 упрощает все, взяв на себя всю тяжелую работу в бэкэнде.

На этот раз мы создаем такой HTML шаблон abc.htmкоторый имеет как минимум следующее:

<repeat group="{{ @result }}" value="{{ @item }}">
    <span>{{ @item.brandName  }}</span>
</repeat>

В большинстве случаев набора команд SQL должно быть достаточно для генерации готового к работе в Интернете результата, чтобы вы могли напрямую использовать переменную resultмассива в своем шаблоне. Как бы то ни было, Fat-Free не помешает вам проникнуть в его внутренние части SQL-обработчика. На самом деле DB\SQLкласс F3 происходит непосредственно от PDOкласса PHP, поэтому у вас все еще есть доступ к базовым компонентам PDO и примитивам, участвующим в каждом процессе, если вам нужен какой-то низкоуровневый контроль.

Операции

Вот еще один пример. Вместо одного оператора, предоставленного в качестве аргумента $db->exec()команде, вы также можете передать массив SQL-операторов:

$db->exec(
    array(
        'DELETE FROM diet WHERE food="cola"',
        'INSERT INTO diet (food) VALUES ("carrot")',
        'SELECT * FROM diet'
    )
);

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

Вы также можете запускать и завершать транзакцию программно:

$db->begin();
$db->exec('DELETE FROM diet WHERE food="cola"');
$db->exec('INSERT INTO diet (food) VALUES ("carrot")');
$db->exec('SELECT * FROM diet');
$db->commit();

Откат произойдет, если какой-либо из операторов столкнется с ошибкой.

Чтобы получить список всех выданных инструкций базы данных:

echo $db->log();

параметризованный запрос

Передача строковых аргументов в SQL-операторы чревата опасностью.

$db->exec(
    'SELECT * FROM users '.
    'WHERE username="'.$f3->get('POST.userID').'"'
);

Если POSTпеременная userIDне проходит через какой-либо процесс очистки данных, злоумышленник может передать следующую строку и необратимо повредить вашу базу данных:

admin"; DELETE FROM users; SELECT "1

К счастью, параметризованные запросы помогают снизить эти риски:

$db->exec(
    'SELECT * FROM users WHERE userID=?',
    $f3->get('POST.userID')
);

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

Наш пример в предыдущем разделе будет намного безопаснее от SQL-инъекции, если он будет написан таким образом:

$db->exec(
    array(
        'DELETE FROM diet WHERE food=:name',
        'INSERT INTO diet (food) VALUES (?)',
        'SELECT * FROM diet'
    ),
    array(
        array(':name'=>'cola'),
        array(1=>'carrot'),
        NULL
    )
);

Вы также можете посмотреть видео, посвященное SQL-инъекции и безопасности базы данных.

Грубость (но с большим количеством стиля)

F3 упакован с простыми в использовании объектно-реляционными сопоставителями (ORM), которые находятся между вашим приложением и вашими данными, что делает его намного проще и быстрее для вас писать программы, которые обрабатывают общие операции с данными, такие как создание, извлечение, обновление и удаление информации (CRUD) из баз данных SQL и NoSQL. Картографы данных выполняют большую часть работы, сопоставляя взаимодействия объектов PHP с соответствующими бэкенд-запросами.

Предположим, у вас есть существующая база данных MySQL, содержащая таблицу пользователей вашего приложения. (SQLite, PostgreSQL, SQL Server, Sybase подойдут точно так же.) Он был бы создан с помощью следующей команды SQL:

CREATE TABLE users (
    userID VARCHAR(30),
    password VARCHAR(30),
    visits INT,
    PRIMARY KEY(userID)
);

Примечание: MongoDB-это движок базы данных NoSQL, который по своей сути не содержит схем. F3 имеет свою собственную быструю и легкую реализацию NoSQL под названием Jig, которая использует файлы, сериализованные на PHP или закодированные в JSON. Эти уровни абстракции не требуют жестких структур данных. Поля могут варьироваться от одной записи к другой. Они также могут быть определены или отброшены на лету.

Теперь вернемся к SQL. Во-первых, мы устанавливаем связь с нашей базой данных.

$db=new DB\SQL(
    'mysql:host=localhost;port=3306;dbname=mysqldb',
    'admin',
    'wh4t3v3r'
);

Чтобы извлечь запись из нашей таблицы:

$user=new DB\SQL\Mapper($db,'users');
$user->load(array('userID=?','tarzan'));

В первой строке создается экземпляр объекта Data mapper, который взаимодействует с usersтаблицей в нашей базе данных. За сценой F3 извлекает структуру usersтаблицы и определяет, какие поля определяются как первичные ключи. На данный момент объект mapper еще не содержит никаких данных (он называется "сухое состояние"), а $uservar - это в основном не более чем структурированный объект, но он содержит методы, необходимые для выполнения основных операций CRUD, плюс некоторые дополнительные функции, как вы увидите позже. Теперь, чтобы извлечь запись из нашей usersтаблицы, например, с полемuserID, содержащим строковое значение tarzan, мы используем load() метод. Этот процесс называется "автогидратацией" объекта Data mapper.

Легко, не правда ли? F3 понимает, что таблица SQL уже имеет структурное определение, существующее в самом ядре СУБД. В отличие от других фреймворков, F3 не требует никаких дополнительных объявлений классов (если только вы не хотите расширить картографы данных, чтобы они соответствовали сложным объектам), никаких избыточных сопоставлений свойств PHP-массива/объекта в поле (дублирование усилий), никаких генераторов кода (которые требуют регенерации кода при изменении структуры базы данных), никаких глупых XML/YAML-файлов для настройки ваших моделей, никаких лишних команд просто для извлечения одной записи. С помощью F3 простое изменение размера a varchar поле в вашей таблице MySQL не требует ни одного изменения в коде вашего приложения. В соответствии с MVC и "разделением проблем" администратор базы данных имеет такой же контроль над данными и структурами, как дизайнер шаблонов над шаблонами HTML/XML.

Если вы предпочитаете работать с базами данных NoSQL, то сходство в синтаксисе запросов поверхностно. В случае картографа данных MongoDB эквивалентным кодом будет:

$db=new DB\Mongo('mongodb://localhost:27017','testdb');
$user=new DB\Mongo\Mapper($db,'users');
$user->load(array('userID'=>'tarzan'));

В jig синтаксис аналогичен шаблонному движку F3:

$db=new DB\Jig('db/data/',DB\Jig::FORMAT_JSON);
$user=new DB\Jig\Mapper($db,'users');
$user->load(array('@userID=?','tarzan'));

Интеллектуальный 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таблице, мы можем добавить следующий код:

$user->visits++;
$user->save();

Если мы хотим вставить запись, мы следуем этому процессу:

$user=new DB\SQL\Mapper($db,'users');
// or $user=new DB\Mongo\Mapper($db,'users');
// or $user=new DB\Jig\Mapper($db,'users');
$user->userID='jane';
$user->password=md5('secret');
$user->visits=0;
$user->save();

Мы все еще используем тот же save()метод. Но как F3 узнает, когда запись должна быть вставлена или обновлена? В тот момент, когда объект картографа данных автоматически гидратируется извлечением записи, фреймворк отслеживает первичные ключи записи (или _id, в случае MongoDB и Jig) - так что он знает, какая запись должна быть обновлена или удалена-даже при изменении значений первичных ключей. Программный гидратированный картограф данных, значения которого не были извлечены из базы данных, но заполнены приложением, не будет иметь памяти предыдущих значений в своих первичных ключах. То же самое относится к MongoDB и Jig, но с использованием объекта _idв качестве ссылки. Итак, когда мы создали экземпляр $user объект выше и заполнил его свойства значениями из нашей программы - вообще не извлекая запись из пользовательской таблицы, F3 знает, что он должен вставить эту запись.

Объект mapper не будет пустым после a save(). Если вы хотите добавить новую запись в свою базу данных, вы должны сначала очистить картограф с помощью этого resetметода:

$user->reset();
$user->userID='cheetah';
$user->password=md5('unknown');
$user->save();

Вызов save()во второй раз без вызова reset()просто обновит запись, на которую в данный момент указывает картограф.

Предостережение для таблиц SQL

Хотя проблема наличия первичных ключей во всех таблицах базы данных является спорной, F3 не мешает вам создать объект сопоставления данных, который взаимодействует с таблицей, не содержащей первичных ключей. Единственный недостаток: вы не можете удалить или обновить сопоставленную запись, потому что F3 абсолютно не может определить, на какую запись вы ссылаетесь, плюс тот факт, что позиционные ссылки ненадежны. Идентификаторы строк не переносимы между различными движками SQL и не могут быть возвращены драйвером базы данных PHP.

Чтобы удалить сопоставленную запись из нашей таблицы, вызовите этот erase()метод на автогидратированном картографе данных. Например:

$user=new DB\SQL\Mapper($db,'users');
$user->load(array('userID=? AND password=?','cheetah','ch1mp'));
$user->erase();

Синтаксис запроса джига был бы немного похож:

$user=new DB\Jig\Mapper($db,'users');
$user->load(array('@userID=? AND @password=?','cheetah','chimp'));
$user->erase();

И эквивалент MongoDB был бы таким:

$user=new DB\Mongo\Mapper($db,'users');
$user->load(array('userID'=>'cheetah','password'=>'chimp'));
$user->erase();

Состояние Данных Картографа

Чтобы узнать, был ли наш картограф данных загружен с допустимой записью данных или нет, используйте этот dryметод:

if ($user->dry())
    echo 'Нет критериев соответствия записей';

За гранью грубости

Мы разобрались с обработчиками грязи. Есть некоторые дополнительные методы, которые вы можете найти полезными:

$f3->set('user',new DB\SQL\Mapper($db,'users'));
$f3->get('user')->copyFrom('POST');
$f3->get('user')->save();

Обратите внимание, что мы также можем использовать обезжиренные переменные в качестве контейнеров для объектов mapper. copyFrom()Метод увлажняет объект mapper элементами из переменной фреймворка массива, ключи массива которого должны иметь имена, идентичные свойствам объекта mapper, которые, в свою очередь, соответствуют именам полей записи. Таким образом, при отправке веб-формы (при условии, что атрибут HTML name установлен в userIDзначение) содержимое этого поля ввода передается$_POST['userID'], дублируется F3 в его POST.userIDпеременной и сохраняется в сопоставленном поле $user->userID в базе данных. Процесс становится очень простым, если все они имеют одинаковые имена элементов. Согласованность в ключах массива, т. е. именах шаблонных маркеров, именах переменных фреймворка и именах полей является ключевой :)Опасность: по умолчанию copyfromиспользуется весь предоставленный массив. Это может привести к утечке данных безопасности, если пользователь разместит больше полей, чем вы ожидаете. Используйте 2-й параметр для настройки функции обратного вызова фильтра, чтобы избавиться от нежелательных полей для копирования.

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

$f3->set('user',new DB\SQL\Mapper($db,'users'));
$f3->get('user')->load(array('userID=?','jane'));
$f3->get('user')->copyTo('POST');

Затем мы можем назначить {{ @POST.userID }} для атрибута value того же поля ввода. Подводя итог, можно сказать, что поле ввода HTML будет выглядеть следующим образом:

<input type="text" name="userID" value="{{ @POST.userID }}">

Методыsave(),update(), copyFrom()Data mapper и параметризованные варианты load()and erase()безопасны от SQL-инъекции.

По умолчанию load()метод сопоставления данных извлекает только первую запись, соответствующую заданным критериям. Если у вас есть несколько записей, удовлетворяющих тому же условию, что и первая загруженная запись, вы можете использовать этот skip()метод для навигации:

$user=new DB\SQL\Mapper($db,'users');
$user->load('visits>3');
// Переписано как параметризованный запрос
$user->load(array('visits>?',3));

// Для пользователей MongoDB:
// $user=new DB\Mongo\Mapper($db,'users');
// $user->load(array('visits'=>array('$gt'=>3)));

// Если вы предпочитаете Jig:
// $user=new DB\Jig\Mapper($db,'users');
// $user->load('@visits>?',3);

// Отобразите идентификатор пользователя первой записи, соответствующей критериям
echo $user->userID;
// Перейдите к следующей записи, соответствующей тем же критериям
$user->skip(); // Так же, как $user->skip(1);
// Вернемся к первой записи
$user->skip(-1);
// Переместите три записи вперед
$user->skip(3);

Вы можете использовать $user->next()в качестве замены$user->skip(), и $user->prev()если вы думаете, что это дает больше смысла$user->skip(-1).

Используйте этот dry()метод, чтобы проверить, не вышли ли вы за пределы результирующего набора. dry() вернет TRUE, если вы попробуете skip(-1)на первой записи. Он также вернет TRUE, если вы skip(1)на последней записи, которая соответствует критериям извлечения.

load()Метод принимает второй аргумент: массив опций, содержащий пары ключ-значение, такие как:

$user->load(
    array('visits>?',3),
    array(
        'order'=>'userID DESC',
        'offset'=>5,
        'limit'=>3
    )
);

Если вы используете MySQL, запрос преобразуется в:

SELECT * FROM users
WHERE visits>3
ORDER BY userID DESC
LIMIT 3 OFFSET 5;

Это один из способов представления данных в виде небольших фрагментов. Вот еще один способ разбиения результатов на страницы:

$page=$user->paginate(2,5,array('visits>?',3));

В приведенном выше сценарии F3 будет извлекать записи, соответствующие этим критериям 'visits>3'. Затем он ограничит результаты 5 записями (на страницу), начиная со смещения страницы 2 (на основе 0). Фреймворк вернет массив состоящий из следующих элементов:

[subset] массив объектов картографа, соответствующих критериям
[total] сумма всех записей для всех страниц
[limit] то же значение, что и параметр размера (здесь 5)
[count] количество доступных подмножеств/страниц
[pos] фактическая позиция подмножества

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

Виртуальные Поля

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

Предположим, что у нас есть следующая таблица, определенная как:

CREATE TABLE products (
    productID VARCHAR(30),
    description VARCHAR(255),
    supplierID VARCHAR(30),
    unitprice DECIMAL(10,2),
    quantity INT,
    PRIMARY KEY(productID)
);

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

$item=new DB\SQL\Mapper($db,'products');
$item->totalprice='unitprice*quantity';
$item->load(array('productID=:pid',':pid'=>'apple'));
echo $item->totalprice;

Приведенный выше фрагмент кода определяет вызываемое виртуальное полеtotalprice, которое вычисляется путем умножения unitpriceна число quantity. SQL mapper сохраняет это правило / формулу, поэтому, когда приходит время извлекать запись из базы данных, мы можем использовать виртуальное поле как обычное отображенное поле.

Вы можете иметь более сложные виртуальные поля:

$item->mostNumber='MAX(quantity)';
$item->load();
echo $item->mostNumber;

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

Вы также можете получить значение из другой таблицы:

$item->supplierName=
    'SELECT name FROM suppliers '.
    'WHERE products.supplierID=suppliers.supplierID';
$item->load();
echo $item->supplierName;

Каждый раз, когда вы загружаете запись из таблицы products, ORM перекрестно ссылается на supplerIDin the productstable с supplierIDin the supplierstable.

Чтобы уничтожить виртуальное поле, используйте unset($item->totalPrice);. isset($item->totalPrice)Выражение возвращает TRUE, если totalPriceвиртуальное поле было определено, или FALSE, если нет.

Помните, что виртуальное поле должно быть определено до получения данных. ORM не выполняет ни собственно вычисление, ни вывод результатов из другой таблицы. Именно компонент database engine выполняет всю тяжелую работу.

Ищите и вы найдете

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

$frequentUsers=$user->find(array('visits>?',3),array('order'=>'userID'));

Синтаксис запросов jig mapper имеет небольшое сходство:

$frequentUsers=$user->find(array('@visits>?',3),array('order'=>'userID'));

Эквивалентный код с использованием картографа MongoDB:

$frequentUsers=$user->find(array('visits'=>array('$gt'=>3)), array('order'=>array('userID'=>1)));

find()Метод ищет в usersтаблице записи, соответствующие критериям, сортирует результат по userIDним и возвращает результат в виде массива объектов mapper. find('visits>3') отличается от load('visits>3')него . Последнее относится к текущему $userобъекту. find() не оказывает на него никакого влияния skip().Важно: Объявление пустого условия, NULL или строки нулевой длины в качестве первого аргумента find()or load()приведет к извлечению всех записей. Убедитесь, что вы знаете, что делаете - вы можете превысить "memory_limit" PHP на больших таблицах или коллекциях

Этот find()метод имеет следующий синтаксис:

find(
    $criteria,
    array(
        'group'=>'foo',
        'order'=>'foo,bar',
        'limit'=>5,
        'offset'=>0
    )
);

функция find() возвращает массив объектов. Каждый объект является сопоставителем записи, соответствующей заданным критериям.:

$place=new DB\SQL\Mapper($db,'places');
$list=$place->find('state="New York"');
foreach ($list as $obj)
    echo $obj->city.', '.$obj->country;

Если вам нужно преобразовать объект mapper в ассоциативный массив, используйте этот cast()метод:

$array=$place->cast();
echo $array['city'].', '.$array['country'];

Чтобы получить количество записей в таблице, соответствующих определенному условию, используйте этот count()метод.

if (!$user->count(array('visits>?',10)))
    echo 'Нам нужна лучшая рекламная кампания!';

Существует также select()метод, который аналогиченfind(), но обеспечивает более мелкозернистый контроль над возвращаемыми полями. Он имеет SQL-подобный синтаксис:

select(
    'foo, bar, baz',
    'foo > ?',
    array(
        'group'=>'foo, bar',
        'order'=>'baz ASC',
        'limit'=>5,
        'offset'=>3
    )
);

Как find()и метод, select()он не изменяет содержимое объекта mapper. Он служит только в качестве удобного метода для запроса сопоставленной таблицы. Возвращаемое значение обоих методов представляет собой массив объектов mapper. Использование dry()для определения того, была ли запись найдена одним из этих методов, неуместно. Если ни одна запись не соответствует критерию find()илиselect(), возвращаемое значение является пустым массивом.Имейте в виду: load() гидратирует текущий объект mapper, findoneвозвращает новый гидратированный объект mapper и findвозвращает массив гидратированных объектов mapper.Опасность: массив $options for не использует параметризованные значения полей и не очищается от входных данных пользователя. Если вы просто помещаете значения GET или POST в группу, ордер, лимит или смещение, не проверяя их заранее, вы открываете проблему безопасности, и могут произойти плохие вещи.

Профилирование

Если вы когда-нибудь захотите выяснить, какие операторы SQL, выпущенные непосредственно вашим приложением (или косвенно через объекты mapper), вызывают узкие места производительности, вы можете сделать это с помощью простого:

echo $db->log();

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

Иногда этого просто недостаточно

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

class Vendor extends DB\SQL\Mapper {
	// Создать экземпляр картографа
	function __construct(DB\SQL $db) {
		// Именно здесь происходит синхронизация картографа и структуры БД
		parent::__construct($db,'vendors');
	}

	// Специализированный запрос
	function listByCity() {
		return $this->select('vendorID,name,city',null,array('order'=>'city DESC'));
		/*
		Мы могли бы сделать то же самое с обычным ванильным SQL:
		return $this->db->exec(
			'SELECT vendorID,name,city FROM vendors '.
			'ORDER BY city DESC;'
		);
		*/
	}
}

$vendor=new Vendor;
$vendor->listByCity();

Расширение карт данных таким образом-это простой способ построить модели, связанные с БД вашего приложения.

плюсы и минусы

Если вы хорошо разбираетесь в SQL, вы, вероятно, скажете: все в ORM можно обрабатывать с помощью SQL-запросов старой школы. Действительно. Мы можем обойтись без дополнительных прослушивателей событий, используя триггеры базы данных и хранимые процедуры. Мы можем выполнять реляционные запросы с Объединенными таблицами. ОРМ - это просто ненужные накладные расходы. Но суть в том, что картографы данных дают вам дополнительную функциональность использования объектов для представления сущностей базы данных. Как разработчик, вы можете писать код быстрее и продуктивнее. Полученная программа будет чище, если не короче. Но вам придется взвесить преимущества против компромисса в скорости - особенно при работе с большими и сложными хранилищами данных. Помните, что все ОРМЫ - какими бы тонкими они ни были - всегда будут просто еще одним абстрактным слоем. Они все еще должны передать работу базовым SQL-движкам.

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

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

Прежде чем объединять несколько объектов в приложении для управления базовыми таблицами в базе данных, подумайте вот о чем: создание представлений для представления связей и триггеров для определения поведения объектов в ядре СУБД более эффективно. Движки реляционных баз данных предназначены для обработки представлений, Объединенных таблиц и триггеров. Это не тупые хранилища данных. Таблицы, объединенные в представление, будут отображаться как одна таблица, и Fat-Free может автоматически отображать представление так же, как и обычную таблицу. Репликация соединений как реляционных объектов в PHP происходит медленнее по сравнению с машинным кодом ядра СУБД, реляционной алгеброй и логикой оптимизации. Кроме того, многократное объединение таблиц в нашем приложении является верным признаком того, что дизайн базы данных нуждается в аудите, а представления считаются неотъемлемой частью поиска данных. Если таблица часто ссылается на данные из другой таблицы, подумайте о нормализации структуры или создании представления. Затем создайте объект mapper для автоматического отображения этого представления. Это быстрее и требует меньше усилий.

Рассмотрим это представление SQL, созданное внутри вашего ядра СУБД:

CREATE VIEW combined AS
    SELECT
        projects.project_id AS project,
        users.name AS name
    FROM projects
    LEFT OUTER JOIN users ON
        projects.project_id=users.project_id AND
        projects.user_id=users.user_id;

Код вашего приложения становится простым, потому что ему не нужно поддерживать два объекта mapper (один для таблицы проектов, а другой для пользователей) только для извлечения данных из двух соединенных таблиц:

$combined=new DB\SQL\Mapper($db,'combined');
$combined->load(array('project=?',123));
echo $combined->name;

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

Last updated