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

时间:2009-01-14 12:41:51

标签: php sql coding-style

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

10 个答案:

答案 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之类的变量。