PHP/MySql - 查询具有嵌套对象的对象的最佳方式
PHP/MySql - Optimal way to querying an object with nested objects
每次我制作新的 OOP 应用程序时,我都会为这个问题而苦恼。假设这将成长为一个中型到大型网站,并且会持续增长,那么实现 selecting 嵌套在另一个对象中的对象的正确方法是什么(在这种情况下,实现匹配信息查询的最佳方法是什么) .
假设我有这个:
Class SportsTeam {
protected $teamId;
protected $teamName;
protected $teamLocation;
}
Class Matchup {
protected $matchupId;
protected $date;
protected $time;
protected $homeTeamId;
protected $awayTeamId;
protected $homeTeam; //SportsTeam object
protected $awayTeam; //SportsTeam object
}
Class SportsTeamDao {
public function getSportsTeam($teamId) { //Get SportTeam info }
}
Class MatchupDao {
public function getMatchup($matchupId) { //Get Matchup info }
}
选项 1:Select比赛对象本身和比赛对象内部调用 homeTeam 对象的 get 方法,并调用 awayTeam 对象以根据需要获取整个 SportsTeam 对象,这样如果我稍后将属性添加到 SportsTeam 对象(例如 teamLogo),它们可以在我使用匹配对象的地方访问。
Class Matchup {
protected $matchupId;
protected $date;
protected $time;
protected $homeTeamId;
protected $awayTeamId;
protected $homeTeam; //SportsTeam object
protected $awayTeam; //SportsTeam object
//Getters and setters for matchupId, date, time, homeTeamId, awayTeamId
public function getHomeTeam() {
$this->homeTeam = SportsTeamDao::getSportsTeam($this->homeTeamId);
return $homeTeam;
}
public function getAwayTeam() {
$this->awayTeam = SportsTeamDao::getSportsTeam($this->awayTeamId);
return $awayTeam;
}
}
Class SportsTeamDao {
public function getSportsTeam($teamId) { //Get SportTeam info }
}
Class MatchupDao {
public function getMatchup($matchupId) {
$query = "SELECT matchupId, date, time, hometeamid, awayteamid WHERE matchupId = $matchupId LIMIT 1"; //I'd parameterize it of course
}
}
选项 2:Select 连接中匹配可能需要的所有详细信息,因此我只访问数据库一次。缺点是,如果我稍后将属性添加到 SportsTeam,我必须记住将其添加到我的 select 语句中,用于 SportsTeam 和 Matchup 以及任何其他需要使用这些新属性的地方。
Class SportsTeam {
protected $teamId;
protected $teamName;
protected $teamLocation;
}
Class Matchup {
protected $matchupId;
protected $date;
protected $time;
protected $homeTeamId;
protected $awayTeamId;
protected $homeTeam; //SportsTeam object
protected $awayTeam; //SportsTeam object
}
Class SportsTeamDao {
public function getSportsTeam($teamId) { //Get SportTeam info }
}
Class MatchupDao {
public function getMatchup($matchupId) {
$query = "SELECT M.matchupid, M.date, M.time, M.homeTeamId, M.awayTeamId, HT.teamName AS homeN, HT.teamLocation AS homeL, AT.teamName AS awayN, AT.teamLocation AS awayL FROM matchups M LEFT JOIN sportsteam HT ON M.hometeamid = HT.teamid LEFT JOIN sportsteam AT ON M.awayteamid = AT.teamid WHERE matchupid = $matchupid LIMIT 1";
//Set matchup properties and hometeam object properties and awayteam object properties
}
}
我的困境是选项 1 的数据库密集度更高(想想如果我想显示球队中的足球运动员列表,它是 1 个球队呼叫 + 1 个获取球队所有球员 ID 的呼叫 + 53 个以上为每个玩家对象调用一个)但更易于管理,选项 2 是更少的数据库连接(1 个团队调用,1 个所有玩家调用)但维护起来更乏味。那么实现这个的正确方法是什么,或者有第三种更好的方法吗?
这个评论太长了,所以这里有一个答案。
选项 1 "structurally" 更好。您主要关心的是繁重的数据库连接,如果您使用 prepared statement 并重新使用 SELECT
语句,可以在某种程度上对其进行优化。
这是我通常做的以确保语句重用(只是一个演示;实际代码中可能会有更多逻辑):
class DB
{
protected $statements=[];
public function prepare($query)
{
if(empty($this->statements[$query]))
{
$this->statements[$query]=$mysqli->prepare($query);
}
return $this->statements[$query];
}
}
因此,每当您准备查询时,数据库实例都会检查该查询之前是否已经准备好,如果已经准备好,return 该语句而不是再次重新准备。
下一步是让您的 classes 依赖于您的数据库 class。您可以通过使用 Singleton(全局命名空间下的静态 DB "variable")来执行此操作:
class Singleton
{
protected static $db;
public static function getDB()
{
if(empty(self::$db))
{
self::$db=new DB(/*...*/);
}
return self::$db;
}
}
或类似这样的内容:
class Car
{
public function __construct(DB $db);
}
您可以根据自己的需要选择合适的方式。
每次我制作新的 OOP 应用程序时,我都会为这个问题而苦恼。假设这将成长为一个中型到大型网站,并且会持续增长,那么实现 selecting 嵌套在另一个对象中的对象的正确方法是什么(在这种情况下,实现匹配信息查询的最佳方法是什么) .
假设我有这个:
Class SportsTeam {
protected $teamId;
protected $teamName;
protected $teamLocation;
}
Class Matchup {
protected $matchupId;
protected $date;
protected $time;
protected $homeTeamId;
protected $awayTeamId;
protected $homeTeam; //SportsTeam object
protected $awayTeam; //SportsTeam object
}
Class SportsTeamDao {
public function getSportsTeam($teamId) { //Get SportTeam info }
}
Class MatchupDao {
public function getMatchup($matchupId) { //Get Matchup info }
}
选项 1:Select比赛对象本身和比赛对象内部调用 homeTeam 对象的 get 方法,并调用 awayTeam 对象以根据需要获取整个 SportsTeam 对象,这样如果我稍后将属性添加到 SportsTeam 对象(例如 teamLogo),它们可以在我使用匹配对象的地方访问。
Class Matchup {
protected $matchupId;
protected $date;
protected $time;
protected $homeTeamId;
protected $awayTeamId;
protected $homeTeam; //SportsTeam object
protected $awayTeam; //SportsTeam object
//Getters and setters for matchupId, date, time, homeTeamId, awayTeamId
public function getHomeTeam() {
$this->homeTeam = SportsTeamDao::getSportsTeam($this->homeTeamId);
return $homeTeam;
}
public function getAwayTeam() {
$this->awayTeam = SportsTeamDao::getSportsTeam($this->awayTeamId);
return $awayTeam;
}
}
Class SportsTeamDao {
public function getSportsTeam($teamId) { //Get SportTeam info }
}
Class MatchupDao {
public function getMatchup($matchupId) {
$query = "SELECT matchupId, date, time, hometeamid, awayteamid WHERE matchupId = $matchupId LIMIT 1"; //I'd parameterize it of course
}
}
选项 2:Select 连接中匹配可能需要的所有详细信息,因此我只访问数据库一次。缺点是,如果我稍后将属性添加到 SportsTeam,我必须记住将其添加到我的 select 语句中,用于 SportsTeam 和 Matchup 以及任何其他需要使用这些新属性的地方。
Class SportsTeam {
protected $teamId;
protected $teamName;
protected $teamLocation;
}
Class Matchup {
protected $matchupId;
protected $date;
protected $time;
protected $homeTeamId;
protected $awayTeamId;
protected $homeTeam; //SportsTeam object
protected $awayTeam; //SportsTeam object
}
Class SportsTeamDao {
public function getSportsTeam($teamId) { //Get SportTeam info }
}
Class MatchupDao {
public function getMatchup($matchupId) {
$query = "SELECT M.matchupid, M.date, M.time, M.homeTeamId, M.awayTeamId, HT.teamName AS homeN, HT.teamLocation AS homeL, AT.teamName AS awayN, AT.teamLocation AS awayL FROM matchups M LEFT JOIN sportsteam HT ON M.hometeamid = HT.teamid LEFT JOIN sportsteam AT ON M.awayteamid = AT.teamid WHERE matchupid = $matchupid LIMIT 1";
//Set matchup properties and hometeam object properties and awayteam object properties
}
}
我的困境是选项 1 的数据库密集度更高(想想如果我想显示球队中的足球运动员列表,它是 1 个球队呼叫 + 1 个获取球队所有球员 ID 的呼叫 + 53 个以上为每个玩家对象调用一个)但更易于管理,选项 2 是更少的数据库连接(1 个团队调用,1 个所有玩家调用)但维护起来更乏味。那么实现这个的正确方法是什么,或者有第三种更好的方法吗?
这个评论太长了,所以这里有一个答案。
选项 1 "structurally" 更好。您主要关心的是繁重的数据库连接,如果您使用 prepared statement 并重新使用 SELECT
语句,可以在某种程度上对其进行优化。
这是我通常做的以确保语句重用(只是一个演示;实际代码中可能会有更多逻辑):
class DB
{
protected $statements=[];
public function prepare($query)
{
if(empty($this->statements[$query]))
{
$this->statements[$query]=$mysqli->prepare($query);
}
return $this->statements[$query];
}
}
因此,每当您准备查询时,数据库实例都会检查该查询之前是否已经准备好,如果已经准备好,return 该语句而不是再次重新准备。
下一步是让您的 classes 依赖于您的数据库 class。您可以通过使用 Singleton(全局命名空间下的静态 DB "variable")来执行此操作:
class Singleton
{
protected static $db;
public static function getDB()
{
if(empty(self::$db))
{
self::$db=new DB(/*...*/);
}
return self::$db;
}
}
或类似这样的内容:
class Car
{
public function __construct(DB $db);
}
您可以根据自己的需要选择合适的方式。