我正在为我的公司开发一个Web应用程序。该应用程序用于跟踪我们购买(和使用)的工具。
每次公司的工作人员使用工具时,都会在应用程序中注册(mysql db)。 到目前为止,我已经用程序风格编写了代码。在OOP中仍然是一个新手,我的程序代码工作正常并且相对较快。
现在应用程序变得越来越大,我真的遇到了维护旧编码模式的问题,因此我认为现在是时候继续尝试实现OOP了。我现在面临的问题是我以某种方式制作了我的课程,因此应用程序比以前慢了10倍。我的一个页面需要1秒才能加载 - 没有类需要0.1秒。 我使用php microtime来测量它。
我创建了一个公司类,其中包含每个公司的属性。 该课程的片段:
class Company {
private $companyId;
private $name;
private $cvr;
etc…..
private $tools = array();
}
到目前为止,它的工作正如预期的那样。现在我想创建一个方法来获取所有的公司工具并将其附加到$ tools数组。
public function tools() {
$purchases = $db
->table('CompanyTools')
->select(
'id'
)
->where('CompanyTools.companyId', '=', $this->companyId)
->get();
$objects = array();
foreach ($purchases as $purchase) {
$objects[] = new CompanyTool($purchase['id']);
}
$this->tools = $objects;
return $this->tools;
}
现在当我像这样使用它时,我得到了提到的性能问题
foreach ($company->tools() as $purchase) {
echo $purchase->id;
}
我怀疑它与创建CompanyTool类的新实例的循环有关。 CompanyTool类看起来像这样
class CompanyTool {
function __construct($id = null) {
if(!$id) die("We need the id");
$this->id = $id;
$attributes = $db
->table('CompanyTools as ct')
->select(
'ct.*'
)
->where('ct.id', '=', $this->id)
->take(1)
->get();
foreach($attributes as $attribute)
foreach ($attribute as $key => $value) {
if(property_exists($this, $key)) $this->{$key} = $value;
}
return $this;
}
我希望很明显我想要实现的目标和缺乏OOP知识:)
感谢您的输入
/ j
答案 0 :(得分:1)
您的方法需要审查很多事情。
其中一些:
答案 1 :(得分:1)
您可以进行简单的重新排列,以节省一些周期:
// get all fields in this query
$purchases = $db
->table('CompanyTools')
->select('ct.*')
->where('CompanyTools.companyId', '=', $this->companyId)
->get();
foreach ($purchases as $purchase) {
// and pass each row to the constructor
$objects[] = new CompanyTool($purchase);
}
// ====
class CompanyTool
{
function __construct(array $attributes)
{
if (!isset($attributes['id'])) die("We need the id");
// no need to perform any query here
foreach ($attributes as $key => $value) {
if (property_exists($this, $key)) $this->{$key} = $value;
}
}
}
答案 2 :(得分:0)
您发布的代码中没有真正的线索来了解您的代码为何比以前慢。我唯一能想到的是你在OOP代码中比在程序代码中做更多的MySQL查询。
当您开始使用OOP时,即使您不打算在特定HTML页面上使用此数据,也可能会在创建对象时完全使用数据库中的数据填充对象。这种方法显然存在缺陷。类只应在实际需要时检索数据。
例如,您有一个列出工具的页面,但每页只显示25个工具。在这种情况下,您不应该检索所有工具并将它们放在类的实例中,只需要25个。
这只是一个例子,但我认为你明白了吗?
检查您执行的所有MySQL查询并不困难。只需回显所有执行到屏幕的查询,或将它们写入文件或数据库,并记录它们花了多长时间。然后,您将能够看到服务器花费时间的位置。
但我的观点是只在实际需要数据时访问数据库,而不仅仅是填充对象。那将毫无意义。
答案 3 :(得分:0)
每次创建CompanyTool
时都会运行查询
$attributes = $db
......这会伤害表现
特别是因为您正在使用某种ORM,所以通常情况下效果好三倍 通过它自己。
如果你需要完全实例化所有工具,你将会想要进行某种连接查询来获取所有数据并在一个查询中实例化它们,否则找出一种方法使用占位符并让它们在需要时填写缺失的细节。
答案 4 :(得分:0)
你肯定发现了你的主要性能损失,每次通过循环时初始化一个新的构造函数是一个非常糟糕的主意,特别是因为它每次构建时都运行一个查询 - 这导致每个对象的完整数据库获取周期您正在初始化的行通常本身并不具有计算成本,但是在更大的记录子集中会导致巨大的性能损失。
更好的处理方法是可能有一个单独的类封装所有工具,在启动时它会运行一个SQL查询,以便在数据库中回退并将其存储到您可以访问的数组中,这只会导致一个DB获取周期。
然后,您可以构建一些访问器方法,使用get_product_by_id($prod_id
,get_product_by_category($prod_cat)
等特定关键字从工具数组中提取数据,以获取所需的项目
一些基本的伪代码可能看起来像这样 - 请注意,您应该将此类实现为单例,因为您只希望有一个CompanyTools数组可用。如果您不知道如何做到这一点,我强烈推荐Gooling Singleton设计模式:
class AllCompanyTools
{
private $tools; // Array to hold all of your tools
function __construct()
{
$query = SELECT * FROM company_tools ORDER BY product_id DESC
$res = // Do your execution and capture these tools into an array
set_results($res); // This method would check the array is not null and then set the locally defned
}
function set_results($results)
{
int i = 0;
// Check size of $res_array, ensure its not null and then load into $tools,
// make sure $tools is an array of CompanyTools
..
// Assign each result into the tools array
foreach($results as $tool)
{
// Insert into the index incrementally with the values from the assoc array
$tools[i] = new CompanyTool($tool[id], $tool[name], $tools[category]);
i++;
}
}
function get_product_by_id($id)
{
foreach($tools as $tool)
{
if($tool->$get_id() == $id)
{
return $tool;
}
}
}
}
您的CompanyTool类将更加简单化
class CompanyTools
{
// Swap for your vars...
private $id;
private $name;
private $category;
// Constructor
function __construct($id_in, $name_in, $cat_in)
{
$id = $id_in;
$name = $name_in;
$category = $cat_in;
}
// Accessors
function get_name() {
return self->$name;
}
.. Other accessors below
}
因为$tools
是CompanyTools
的数组,我们可以简单地检查foreach循环中该工具是否与请求参数具有相同的id,如果是,我们将从数组中返回它,而不是连接到我们的数据库处理事务,返回结果,然后终止显着更昂贵的事务。
注意请注意上面的代码不是最终的解决方案,您需要做一些工作来构建CompanyTools数组,我只想展示如何处理性能它将使您有利于执行一个初始提取请求,然后使用该存储的数组来获取所需的数据,而无需与数据库保持连接。