繁体   English   中英

你如何在PHP脚本(编码风格)中嵌入你的SQL查询?

[英]How do you embedded your sql queries in php scripts (coding-style)?

你如何在PHP中嵌入你的SQL脚本? 你只是用字符串或heredoc写它们还是将它们外包给sql文件? 什么时候外包它们有什么最佳实践? 是否有一种优雅的方式来组织这个?

使用具有ORM(对象 - 关系映射)层的框架。 这样您就不必将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。 但我不会将它用于简单的查询。 这并不重要。 主要是“永远不要忘记逃避价值观”

你应该总是真的总是使用带有占位符的prepare语句来表示你的变量。

它的代码略多,但它在大多数数据库上运行效率更高,并保护您免受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";

连接所有字符串可能需要花费更长的时间,但我认为它看起来更好,更容易编辑。

当然,对于极长且专门的查询,读取.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。 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

我喜欢这种格式。 之前的评论中提到过,但对齐似乎对我不利。

$query = "SELECT "
       . " foo, "
       . " bar "
       . "FROM "
       . " mytable "
       . "WHERE "
       . " id = $userid";

易于阅读和理解。 点与正号对齐,使所有内容保持整齐。

我喜欢将SQL保存在单独的文件中的想法,虽然我不确定如何在上面的示例中使用$ userid之类的变量。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM