奋斗与OOP概念 [英] Struggling With OOP Concept

查看:111
本文介绍了奋斗与OOP概念的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



请允许我用伪PHP代码来解释这个问题。

$

b
$ b

假设您有一个user类,它从它的构造函数中的 users 表中加载数据:

  class User {
public $ name;
public $ height;

public function __construct($ user_id){
$ result =查询$ user_id
$ this-> name =`users`表有`user_id' $ result ['name'];
$ this-> height = $ result ['height'];
}
}

简单,真棒。



现在,我们有一个group类,它从 groups 表加载 groups_users 表,并从返回的 user_id 中创建用户对象:

  class Group {
public $ type;
public $ schedule;
public $ users;

public function __construct($ group_id){
$ result =查询`groups`表,加入`groups_users`表,
其中`group_id`= $ group_id
$ this-> type = $ result ['type'];
$ this-> schedule = $ result ['schedule'];

foreach($ result ['user_ids'] as $ user_id){
//创建用户对象
$ users [] = new User($ user_id);
}
}
}

的用户。



美丽,优雅,惊人...在纸上。然而,实际上,创建一个新的组对象...

  $ group = new Group(21); //获取第21组,其中有4个用户

...执行5次查询,而不是更糟糕的是,如果我创建一个 community 类,其中有许多组,每个组中都有很多用户



不适合我的解决方案



多年来,我的方法是不以上述方式编写代码,而是在创建时,我会将表添加到 groups_users 表中用户表以及在对象中创建一个用户对象类数组数组(从不使用/触摸用户 class):

  class Group {
public $ type;
public $ schedule;
public $ users;

public function __construct($ group_id){
$ result =查询`groups`表,加入`groups_users`表,
**并加入`users' table,**
其中`group_id`= $ group_id
$ this-> type = $ result ['type'];
$ this-> schedule = $ result ['schedule'];

foreach($ result ['users'] as $ user){
//创建用户数组
$ users [] = array_of_user_data_crafted_from_the_query_result;
}
}
}

当然,如果我创建一个社区类,在其构造函数中,我需要加入 communities 表与 communities_groups 表与用户 groups_users c $ c> table。



...如果我创建一个城市类,在它的构造函数中,我需要加入城市表与 cities_communities 表与 communities 表与 groups_users 表中的表中的



我必须选择美丽的OOP代码与一百万查询VS。 1查询和手写这些连接为每个单个超集?有没有自动化的系统?



我使用CodeIgniter,并查看无数的其他MVC和项目,找到一个很好的例子,任何人使用模型,而不诉诸于我概述的两个有缺陷的方法之一。



看起来这从来没有做过。 >

我的一个同事正在编写一个框架,正是这样 - 你创建一个包含数据模型的类。其他较高模型可以包含该单一模型,并且它制作和自动化表连接以创建较高模型,包括对象实例化降低模式,所有这些都在单个查询中。他声称他从来没有看到过这样做的框架或系统。



请注意:
我确实总是使用单独的逻辑和持久化类。 (VOs和DAOs - 这是MVC的整个点)。我只是把这两个在这个思想实验中结合起来,在一个类MVC的架构之外,为了简单起见。请放心,无论逻辑和持久性如何分离,此问题仍然存在。我相信本文,介绍我在詹姆斯在下面的评论这个问题,似乎表明,我提出的解决方案(我一直在追踪多年)是,事实上,开发人员目前做什么来解决这个问题。然而,这个问题试图找到自动化确切解决方案的方法,因此并不总是需要为每个超集手动编码。从我可以看到,这从来没有在PHP之前,我的同事的框架将是第一个这样做,除非有人可以指向我的一个。



当然,我从来不会在构造函数中加载数据,我只调用我创建的load()方法,当我真正需要的数据。然而,这与这个问题无关,因为在这个思想实验中(以及在真实的情况下,我需要自动化),我总是需要急切加载所有子子集的位置,并根据需要延迟加载它们在未来的某个时间点。思想实验是简洁的 - 它不遵循最佳实践是一个可笑的点,并且试图解决其布局的答案同样失去了点。



EDIT:为了清楚起见,这里是一个数据库模式。

  CREATE TABLE` b`group_id` int(11)NOT NULL,< - 自动递增
`make` varchar(20)NOT NULL,
`model` varchar(20)NOT NULL


CREATE TABLE`groups_users`(< - 关系表(许多用户到一个组)
`group_id` int(11)NOT NULL,
`user_id` int )NOT NULL



CREATE TABLE`users'(
`user_id` int(11)NOT NULL,< - 自动递增
`name` varchar(20)NOT NULL,
`height` int(11)NOT NULL,

$ b b

(另请注意,我最初使用的概念 wheel s和 car

p>我最终找到一个PHP ORM做到这一点。它是 Laravel的雄辩。您可以指定模型之间的关系,并使用以下语法智能构建用于热切加载的优化查询:

 组:: with('users') - > get(); 

这是一个绝对的救命。我没有必要写一个单一的查询。

解决方案

ActiveRecord在Rails中实现了概念的延迟加载,即延迟数据库查询,直到你真正需要的数据。因此,如果你实例化一个 my_car = Car.find(12)对象,它只查询那一行的cars表。如果以后你想要 my_car.wheels ,那么它会查询车轮表。



以便不加载构造函数中的每个关联对象。汽车构造函数应该只查询汽车,并且应该有一个方法来查询它的所有车轮,另一个查询它的经销商,它只查询经销商,并延迟收集所有其他经销商的汽车,直到你具体说明如 my_car.dealership.cars



Postscript



ORM是数据库抽象层,因此必须调整它们以便于查询,而不是微调。它们允许您快速构建查询。如果以后您决定需要微调查询,则可以切换到发出原始sql命令,或尝试优化您正在获取的对象数量。这是Rails中开始进行性能调整时的标准做法 - 查找在使用原始sql发出时更高效的查询,并且还需要寻找避免在需要之前急速加载(与延迟加载相反)对象的方法。


I'm really struggling with a recurring OOP / database concept.

Please allow me to explain the issue with pseudo-PHP-code.

Say you have a "user" class, which loads its data from the users table in its constructor:

class User {
    public $name;
    public $height;

    public function __construct($user_id) {
        $result = Query the database where the `users` table has `user_id` of $user_id
        $this->name= $result['name'];
        $this->height = $result['height'];
    }
}

Simple, awesome.

Now, we have a "group" class, which loads its data from the groups table joined with the groups_users table and creates user objects from the returned user_ids:

class Group {
    public $type;
    public $schedule;
    public $users;

    public function __construct($group_id) {
        $result = Query the `groups` table, joining the `groups_users` table,
                    where `group_id` = $group_id
        $this->type = $result['type'];
        $this->schedule = $result['schedule'];

        foreach ($result['user_ids'] as $user_id) {
            // Make the user objects
            $users[] = new User($user_id);
        }
    }
}

A group can have any number of users.

Beautiful, elegant, amazing... on paper. In reality, however, making a new group object...

$group = new Group(21);  // Get the 21st group, which happens to have 4 users

...performs 5 queries instead of 1. (1 for the group and 1 for each user.) And worse, if I make a community class, which has many groups in it that each have many users within them, an ungodly number of queries are ran!

The Solution, Which Doesn't Sit Right To Me

For years, the way I've got around this, is to not code in the above fashion, but instead, when making a group for instance, I would join the groups table to the groups_users table to the users table as well and create an array of user-object-like arrays within the group object (never using/touching the user class):

class Group {
    public $type;
    public $schedule;
    public $users;

    public function __construct($group_id) {
        $result = Query the `groups` table, joining the `groups_users` table,
                    **and also joining the `users` table,**
                    where `group_id` = $group_id
        $this->type = $result['type'];
        $this->schedule = $result['schedule'];

        foreach ($result['users'] as $user) {
            // Make user arrays
            $users[] = array_of_user_data_crafted_from_the_query_result;
        }
    }
}

...but then, of course, if I make a "community" class, in its constructor I'll need to join the communities table with the communities_groups table with the groups table with the groups_users table with the users table.

...and if I make a "city" class, in its constructor I'll need to join the cities table with the cities_communities table with the communities table with the communities_groups table with the groups table with the groups_users table with the users table.

What an unmitigated disaster!

Do I have to choose between beautiful OOP code with a million queries VS. 1 query and writing these joins by hand for every single superset? Is there no system that automates this?

I'm using CodeIgniter, and looking into countless other MVC's, and projects that were built in them, and cannot find a single good example of anyone using models without resorting to one of the two flawed methods I've outlined.

It appears this has never been done before.

One of my coworkers is writing a framework that does exactly this - you create a class that includes a model of your data. Other, higher models can include that single model, and it crafts and automates the table joins to create the higher model that includes object instantiations of the lower model, all in a single query. He claims he's never seen a framework or system for doing this before, either.

Please Note: I do indeed always use separate classes for logic and persistence. (VOs and DAOs - this is the entire point of MVCs). I have merely combined the two in this thought-experiment, outside of an MVC-like architecture, for simplicity's sake. Rest assured that this issue persists regardless of the separation of logic and persistence. I believe this article, introduced to me by James in the comments below this question, seems to indicate that my proposed solution (which I've been following for years) is, in fact, what developers currently do to solve this issue. This question is, however, attempting to find ways of automating that exact solution, so it doesn't always need to be coded by hand for every superset. From what I can see, this has never been done in PHP before, and my coworker's framework will be the first to do so, unless someone can point me towards one that does.

And, also, of course I never load data in constructors, and I only call the load() methods that I create when I actually need the data. However, that is unrelated to this issue, as in this thought experiment (and in the real-life situations where I need to automate this), I always need to eager-load the data of all subsets of children as far down the line as it goes, and not lazy-load them at some future point in time as needed. The thought experiment is concise -- that it doesn't follow best practices is a moot point, and answers that attempt to address its layout are likewise missing the point.

EDIT : Here is a database schema, for clarity.

CREATE TABLE `groups` (
  `group_id` int(11) NOT NULL,  <-- Auto increment
  `make` varchar(20) NOT NULL,
  `model` varchar(20) NOT NULL
)

CREATE TABLE `groups_users` ( <-- Relational table (many users to one group)
  `group_id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL
)


CREATE TABLE `users` (
  `user_id` int(11) NOT NULL, <-- Auto increment
  `name` varchar(20) NOT NULL,
  `height` int(11) NOT NULL,
)

(Also note that I originally used the concepts of wheels and cars, but that was foolish, and this example is much clearer.)

SOLUTION:

I ended up finding a PHP ORM that does exactly this. It is Laravel's Eloquent. You can specify the relationships between your models, and it intelligently builds optimized queries for eager loading using syntax like this:

Group::with('users')->get();

It is an absolute life saver. I haven't had to write a single query. It also doesn't work using joins, it intelligently compiles and selects based on foreign keys.

解决方案

ActiveRecord in Rails implements the concept of lazy loading, that is deferring database queries until you actually need the data. So if you instantiate a my_car = Car.find(12) object, it only queries the cars table for that one row. If later you want my_car.wheels then it queries the wheels table.

My suggestion for your pseudo code above is to not load every associated object in the constructor. The car constructor should query for the car only, and should have a method to query for all of it's wheels, and another to query it's dealership, which only queries for the dealership and defers collecting all of the other dealership's cars until you specifically say something like my_car.dealership.cars

Postscript

ORMs are database abstraction layers, and thus they must be tuned for ease of querying and not fine tuning. They allow you to rapidly build queries. If later you decide that you need to fine tune your queries, then you can switch to issuing raw sql commands or trying to otherwise optimize how many objects you're fetching. This is standard practice in Rails when you start doing performance tuning - look for queries that would be more efficient when issued with raw sql, and also look for ways to avoid eager loading (the opposite of lazy loading) of objects before you need them.

这篇关于奋斗与OOP概念的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆