如何在php中嵌入sql脚本?你只是用字符串或heredoc写它们还是将它们外包给sql文件?什么时候外包它们有什么最佳实践?是否有一种优雅的方式来组织这个?
答案 0 :(得分:5)
使用带有ORM(对象 - 关系映射)层的框架。这样您就不必将SQL直接放在任何地方。嵌入式SQL很糟糕,可读性,维护性和一切。
答案 1 :(得分:4)
永远记得逃避输入。不要手动执行,使用预准备语句。以下是我的报告类的示例方法。
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的需要。
答案 2 :(得分:2)
这取决于查询大小和难度。
我个人喜欢heredocs。但我不会将它用于简单的查询。 这并不重要。主要的是“永远不要忘记逃避价值观”
答案 3 :(得分:2)
你应该总是真的一直使用带有占位符的预备语句作为你的变量。
它的代码略多,但它在大多数数据库上运行效率更高,并可以防止SQL注入攻击。
答案 4 :(得分:2)
我更喜欢这样:
$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";
连接所有字符串可能需要花费更长的时间,但我认为它看起来更好,更容易编辑。
当然,对于极长且专门的查询,读取.sql文件或使用存储过程是有意义的。根据您的框架,这可能很简单:
$sql = (string) View::factory('sql/myfile');
(如果需要,可以选择在视图/模板中分配变量)。如果没有模板引擎或框架的帮助,您可以使用:
$sql = file_get_contents("myfile.sql");
希望这有帮助。
答案 5 :(得分:1)
我通常把它们写成函数参数:
db_exec ("SELECT ...");
除非sql非常大的情况,我将其作为变量传递:
$SQL = "SELECT ...";
$result = db_exec ($SQL);
(我使用包装函数或对象进行数据库操作)
答案 6 :(得分:0)
$sql = sprintf("SELECT * FROM users WHERE id = %d", mysql_real_escape_string($_GET["id"]));
从MySQL注入安全
答案 7 :(得分:0)
您可以使用ORM或sql字符串构建器,但是一些复杂的查询需要编写sql。在编写sql时,正如MichałSłaby所说,使用查询绑定。查询绑定可防止sql注入并保持可读性。至于查询的位置:使用模型类。
答案 8 :(得分:0)
一旦达到某个级别,您就会意识到您编写的99%的SQL可以实现自动化。如果你写了很多你想到的属性文件的查询,你可能会做一些更简单的事情:
我们程序员所做的大部分工作都是CRUD:创建读取更新删除
作为我自己的工具,我构建了Pork.dbObject。 Object Relationper Mapper + Active Record in 2个简单类(Database Abstraction + dbObject class)
我网站上的几个例子:
创建一个博客:
$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 +所有回复:
$weblog = new Weblog(1); //Fetches the row with primary key 1 from table weblogs and hooks it's 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
哦,是的,我还为我的脚手架工具创建了一个基本的GUI:Pork.Generator
答案 9 :(得分:0)
我喜欢这种格式。在之前的评论中提到过,但对齐似乎不适合我。
$query = "SELECT "
. " foo, "
. " bar "
. "FROM "
. " mytable "
. "WHERE "
. " id = $userid";
易于阅读和理解。这些点与等号对齐,使所有内容保持整齐。
我喜欢将SQL保存在单独的文件中的想法,尽管我不确定如何在上面的示例中使用$ userid之类的变量。