Ответ 1
Используйте фреймворк с уровнем ORM (Object-Relational Mapping). Таким образом, вам не нужно размещать прямой SQL в любом месте. Встроенный SQL отстой для удобочитаемости, обслуживания и всего.
как вы встраиваете свои sql-скрипты в php? Вы просто пишете их в строке или heredoc или передаете их в файл sql? Существуют ли какие-либо рекомендации, когда их можно передать на аутсорсинг? Есть ли элегантный способ организовать это?
Используйте фреймворк с уровнем ORM (Object-Relational Mapping). Таким образом, вам не нужно размещать прямой SQL в любом месте. Встроенный SQL отстой для удобочитаемости, обслуживания и всего.
Всегда помните, чтобы избежать ввода. Не делайте это вручную, используйте подготовленные заявления. Вот пример метода из моего класса отчетов.
public function getTasksReport($rmId, $stage, $mmcName) {
$rmCondition = $rmId ? 'mud.manager_id = :rmId' : 'TRUE';
$stageCondition = $stage ? 't.stage_id = :stageId' : 'TRUE';
$mmcCondition = $mmcName ? 'mmcs.username = :mmcName' : 'TRUE';
$sql = "
SELECT
mmcs.id AS mmc_id,
mmcs.username AS mmcname,
mud.band_name AS mmc_name,
t.id AS task_id,
t.name AS task,
t.stage_id AS stage,
t.role_id,
tl.id AS task_log_id,
mr.role,
u.id AS user_id,
u.username AS username,
COALESCE(cud.full_name, bud.band_name) AS user_name,
DATE_FORMAT(tl.completed_on, '%d-%m-%Y %T') AS completed_on,
tl.url AS url,
mud.manager_id AS rm_id
FROM users AS mmcs
INNER JOIN banduserdetails AS mud ON mud.user_id = mmcs.id
LEFT JOIN tasks AS t ON 1
LEFT JOIN task_log AS tl ON tl.task_id = t.id AND tl.mmc_id = mmcs.id
LEFT JOIN mmc_roles AS mr ON mr.id = t.role_id
LEFT JOIN users AS u ON u.id = tl.user_id
LEFT JOIN communityuserdetails AS cud ON cud.user_id = u.id
LEFT JOIN banduserdetails AS bud ON bud.user_id = u.id
WHERE mmcs.user_type = 'mmc'
AND $rmCondition
AND $stageCondition
AND $mmcCondition
ORDER BY mmcs.id, t.stage_id, t.role_id, t.task_order
";
$pdo = new PDO(.....);
$stmt = $pdo->prepare($sql);
$rmId and $stmt->bindValue('rmId', $rmId); // (1)
$stage and $stmt->bindValue('stageId', $stage); // (2)
$mmcName and $stmt->bindValue('mmcName', $mmcName); // (3)
$stmt->execute();
return $stmt->fetchAll();
}
В строках, обозначенных (1), (2) и (3), вы увидите способ условной привязки.
Для простых запросов я использую структуру ORM, чтобы уменьшить необходимость создания SQL вручную.
Это зависит от размера и сложности запроса.
Мне лично нравится heredocs. Но я не использую его для простых запросов. Это не важно. Главное "Никогда не забывайте избегать значений"
Вы всегда должны действительно действительно ВСЕГДА использовать инструкции подготовки с держателями мест для своих переменных.
Его немного больше кода, но он работает более эффективно на большинстве БД и защищает вас от атак SQL-инъекций.
Я предпочитаю как таковой:
$sql = "SELECT tbl1.col1, tbl1.col2, tbl2.col1, tbl2.col2"
. " FROM Table1 tbl1"
. " INNER JOIN Table2 tbl2 ON tbl1.id = tbl2.other_id"
. " WHERE tbl2.id = ?"
. " ORDER BY tbl2.col1, tbl2.col2"
. " LIMIT 10, 0";
Может потребоваться немного времени на PHP, чтобы объединить все строки, но я думаю, что это выглядит намного приятнее и легче редактировать.
Конечно, для чрезвычайно длинных и специализированных запросов было бы целесообразно прочитать файл .sql или использовать хранимую процедуру. В зависимости от вашей структуры это может быть просто:
$sql = (string) View::factory('sql/myfile');
(дает вам возможность назначать переменные в представлении/шаблоне, если необходимо). Без помощи механизма шаблонов или фреймворка вы должны использовать:
$sql = file_get_contents("myfile.sql");
Надеюсь, что это поможет.
Я обычно пишу их как аргумент функции:
db_exec ("SELECT ...");
За исключением случаев, когда sql будет очень большим, я передаю его как переменную:
$SQL = "SELECT ...";
$result = db_exec ($SQL);
(Я использую функции-обертки или объекты для операций с базой данных)
$sql = sprintf("SELECT * FROM users WHERE id = %d", mysql_real_escape_string($_GET["id"]));
Безопасный запуск MySQL
Вы можете использовать ORM или построитель строк sql, но некоторые сложные запросы требуют написания sql. При написании sql, как показывает Michał Słaby, используйте привязки запросов. Связывание запросов предотвращает внедрение sql и поддерживает читаемость. Что касается того, где поставить ваши запросы: использовать классы моделей.
Как только вы доберетесь до определенного уровня, вы поймете, что 99% написанного SQL могут быть автоматизированы. Если вы пишете столько запросов, которые вы думаете о файле свойств, вы, вероятно, делаете то, что может быть проще:
Большая часть материала, который мы программисты, это CRUD: Create Read Update Delete
В качестве инструмента для себя я создал Pork.dbObject. Объектная привязка Mapper + Active Запись в 2 простых классах (класс абстракции базы данных + класс dbObject)
Несколько примеров из моего сайта:
Создать веб-журнал:
$weblog = new Weblog(); // create an empty object to work with.
$weblog->Author = 'SchizoDuckie'; // mapped internally to strAuthor.
$weblog->Title = 'A test weblog';
$weblog->Story = 'This is a test weblog!';
$weblog->Posted = date("Y-m-d H:i:s");
$weblog->Save(); // Checks for any changed values and inserts or updates into DB.
echo ($weblog->ID) // outputs: 1
И один ответ на него:
$reply = new Reply();
$reply->Author = 'Some random guy';
$reply->Reply = 'w000t';
$reply->Posted = date("Y-m-d H:i:s");
$reply->IP = '127.0.0.1';
$reply->Connect($weblog); // auto-saves $reply and connects it to $weblog->ID
И, выборка и отображение веб-журнала + все ответы:
$weblog = new Weblog(1); //Fetches the row with primary key 1 from table weblogs and hooks it values into $weblog;
echo("<h1>{$weblog->Title}</h1>
<h3>Posted by {$weblog->Author} @ {$weblog->Posted}</h3>
<div class='weblogpost'>{$weblog->Story}</div>");
// now fetch the connected posts. this is the real magic:
$replies = $weblog->Find("Reply"); // fetches a pre-filled array of Reply objects.
if ($replies != false)
{
foreach($replies as $reply)
{
echo("<div class='weblogreply'><h4>By {$reply->Author} @ {$reply->Posted}</h4> {$reply->Reply}</div>");
}
}
Объект weblog будет выглядеть так:
class Weblog extends dbObject
{
function __construct($ID=false)
{
$this->__setupDatabase('blogs', // database table
array('ID_Blog' => 'ID', // database field => mapped object property
'strPost' => 'Story', // as you can see, database field strPost is mapped to $this->Story
'datPosted' => 'Posted',
'strPoster' => 'Author',
'strTitle' => 'Title',
'ipAddress' => 'IpAddress',
'ID_Blog', // primary table key
$ID); // value of primary key to init with (can be false for new empty object / row)
$this->addRelation('Reaction'); // define a 1:many relation to Reaction
}
}
Смотрите, никаких ручных SQL-писем:) Ссылка + еще примеры: Pork.dbObject
О да, я также создал рудиментарный графический интерфейс для моего инструмента для строительных лесов: Pork.Generator
Мне нравится этот формат. Это было упомянуто в предыдущем комментарии, но выравнивание показалось мне.
$query = "SELECT "
. " foo, "
. " bar "
. "FROM "
. " mytable "
. "WHERE "
. " id = $userid";
Достаточно легко читать и понимать. Точки выстраиваются в линию с помощью знака равенства, сохраняя все в чистой строке.
Мне нравится идея держать ваш SQL в отдельном файле, хотя я не уверен, как это работает с переменными типа $userid в моем примере выше.