简体   繁体   English

使用Mysql数据库的OOP PHP

[英]OOP PHP with Mysql Database

If we have a code like this: 如果我们有这样的代码:

class Game {
    private $_id;
    private $_name;
    private $_url;

    public function __construct($_id,$_name,$_url){
        $this->_id = $_id;
        $this->_name = $_name;
        $this->_url = $_url;
    }
}

And we want to simply connect to our Database to get a game by id, where do we place the 'getByID' function? 我们想简单地连接到我们的数据库以通过id获取游戏,我们在哪里放置'getByID'功能?

Do we place it within the 'Game Class' as 'static function', do we put it in the 'Database Connection Class' as 'public function' or do we just put the method in the 'general functions inside the main index.php' as 'function'? 我们将它作为“静态函数”放在“游戏类”中,我们将它作为“公共函数”放在“数据库连接类”中,还是将该方法放在主索引中的'常规函数中? '作为'功能'?

I currenctly have choosen for a 'static function' within the 'Game Class': 我当前已选择'游戏类'中的'静态功能':

public static function getByID($id,$db){    
    $query = "SELECT * FROM game WHERE id = :id LIMIT 1";
    $prepare = array(":id"=>$id);
    $result = $db->Precute($query,$prepare);
    foreach($result as $r) return new Game($r['id'],$r['name'],$r['url']);
    return null;
}

(Precute is a custom function within the Database Class to prepare and execute the query) How would you approach this? (Precute是数据库类中的自定义函数,用于准备和执行查询)您将如何处理此问题?

In proper OOP, a DAL function which returns an instance of a specific class should be static within that class. 在适当的OOP中,返回特定类的实例的DAL函数在该类中应该是静态的。 As a base rule, all functionality related to one specific object should be part of that specific object, as an instance method if invoked on instances or a static method if it creates or manages instances ('factory pattern'). 作为基本规则,与特定对象相关的所有功能都应该是该特定对象的一部分,如果在实例上调用则为实例方法,如果是创建或管理实例,则为静态方法('工厂模式')。

Your function isn't static currently, correct usage would be: 您的功能目前不是静态的,正确的用法是:

class Game
{
   ..other functions..

   public static function getById($id)
   {
      ..implementation, which can either access central storage or retrieve
        the object itself if concurrent edits are not an issue..
   }
}

Then elsewhere: 其他地方:

$myGame = Game::getById(684);

You may want to have a look at Doctrine instead of re-inventing the wheel. 你可能想看看Doctrine,而不是重新发明轮子。 And even if you do want to make a new wheel, its code samples all follow correct OOP principles. 即使您确实想要制作一个新的轮子,它的代码样本都遵循正确的OOP原则。

This Answer takes another approach. 本答案采用另一种方法。 Instead of getting Objects from Static Factory. 而不是从静态工厂获取对象。 This solution takes a approach of creating a blank object and then calling the database methods to make the object a live representation of a actual row. 此解决方案采用创建空白对象的方法,然后调用数据库方法以使对象成为实际行的实时表示。

first the observations from your question - 首先是你问题的观察结果 -

an Object/Instance of Game class represents a Row of Table game . Game类的对象/实例表示一排桌面game And the Game class itself can be taken as a representation of `game' table. Game类本身可以作为“游戏”表的表示。

If the above observation is correct along with the assumption that there are more tables with a representation in class hierarchy. 如果上述观察结果是正确的,并假设在类层次结构中有更多具有表示的表。 You should have a class to represent generic 'Table' 你应该有一个类来表示泛型'Table'

class Table {   //The class itself can be made abstract depending upon the exact implementation 
   protected $_tableName;
   protected $_connectionParams;
   protected $idAttribute = 'id';

   public function __construct($tableName, $connectionParams, $idAttribute){
       $this->_connectionParams = $connectionParams;
       $this->_tableName = $tableName;
       if(isset($idAttribute)) {
          $this->idAttribute = $idAttribute;
       }
   };

   private function _getConnection() {
      //return $db using $_connectionParams
   };

   public function getByID($id) {    
      $this->getByKeyVal($this->idAttribute, $id);
   };

   public function getByKeyVal($key, $val) {
      $query = "SELECT * FROM ". $this->_tableName ." WHERE `". $key ."` = :key LIMIT 1";
      $prepare = array(":key"=> $val);
      $result = $this->_getConnection()->Precute($query,$prepare);
      $this->processRow($result[0]);
   };

   //This needs to be overridden
   public function processRow($row) {
      return true;
   };
}

Now extend the generic Table class for Game Table 现在扩展Game Table的通用Table类

class Game extends Table {
   private $_id;
   private $_name;
   private $_url;
   public function __construct($defaults) {
      if(isset($defaults) {
         if(is_array($defaults)) {
            $this->processRow($defaults);
         } else {  
            $this->getByID($defaults);
         }
      } else {
         //Some default setup here if requried
      }
      $connectionParams = [];  //Prepare Connection Params here
      parent::__construct('game', $connectionParams);
   };

   //Override processRow
   public function processRow($row) {    
      if(isset($row['id']) {
         $this->_id = $row['id'];
      }
      $this->_name = $row['name'];
      $this->_url = $row['url'];
   };
}

Above is a very rough example. 以上是一个非常粗略的例子。 The actual Class structure will depend upon your requirements. 实际的类结构将取决于您的要求。 But the general rule of thumb is to treat a Class as a blueprint of a concrete object. 但一般的经验法则是将Class视为具体对象的蓝图。 And all the methods related with a Generic Classification should go in there own class. 与通用分类相关的所有方法都应该在自己的类中。

The getConnection Method itself can be put into a seprate DB connection class and inserted in table via a either mixin pattern or generic class inheritance. getConnection方法本身可以放入一个单独的DB连接类,并通过mixin模式或泛型类继承插入到表中。

Use the above setup like this 像这样使用上面的设置

$game_new = new Game();  // for blank object  --- for a new row

$game_435 = new Game(435);    //row with 435 ID

$game_default = new Game(array(    //new row with defaults
  'name' => 'Some Name',
  'url' => 'Some Url'
));

What you want is a "bucket" full of Game objects. 你想要的是一个装满Game对象的“桶”。 When ever you want a Game Object (representing data in your database), you ask your "bucket" to give it to you. 当你想要一个游戏对象(代表数据库中的数据)时,你会要求你的“桶”给你。 Let me give you an example of how Doctrine2 implements this: http://docs.doctrine-project.org/en/2.0.x/reference/working-with-objects.html 让我举一个Doctrine2如何实现这个的例子: http//docs.doctrine-project.org/en/2.0.x/reference/working-with-objects.html

So where you want to place your "getById" (or as I would do "findById"), is in your "bucket". 因此,您想要放置“getById”(或者我将“findById”)放在您的“桶”中。

// lets presume that the em is an instance of \Doctrine\ORM\EntityManager
// The entity manager does what the name says.
$id = 1234;
$game = $entity_manager->find('MyNamespace\Entity\Game', $id);
$game->setName('My first game!');
// We now tell the em to prepare the object for pushing it back to the "bucket" or database
$entity_manager->persist($game);
// Now we tell the em to actually save stuff
$entity_manager->flush();

This should give you an indication of how to use it. 这应该会告诉您如何使用它。 Objects follow the Single Responsibility Principle . 对象遵循单一责任原则 You don't ask an object to retrieve itself. 您不要求对象检索自己。 You ask the "bucket" to retrieve you an Object. 你要求“桶”检索一个对象。

http://en.wikipedia.org/wiki/Single_responsibility_principle http://en.wikipedia.org/wiki/Single_responsibility_principle

What if I told you that there are more beautiful ways to put things on their places. 如果我告诉你有更多美丽的方式把东西放在他们的位置怎么办? A very simple case might contain 3 basic components to work: 一个非常简单的案例可能包含3个基本组件:

  1. Db framework - Which handles data access. Db框架 - 处理数据访问。
  2. Table repsotor classes - Which know how to map classes to tables, how to create classes from table data and how to create data from table classes. 表repsotor类 - 知道如何将类映射到表,如何从表数据创建类以及如何从表类创建数据。
  3. Model or business layer which contain actual classes. 包含实际类的模型或业务层。

For better understanding imagine you have database object mapper framework. 为了更好地理解,您可以设想数据库对象映射器框架。 The framework can be far complex but in few lines we can demonstrate how it`s basic concepts work. 该框架可能非常复杂,但我们可以用几行来证明它的基本概念是如何工作的。

So the 'Framework': 所以'框架':

<?php

//This class is for making link for db framework
class link
{
    public $link;
    public function __construct ($hostname, $database, $gamename, $password)
    {
        $this->link = new \PDO ('mysql:host='.$hostname.';dbname='.$database, $gamename, $password);
        $this->link->query('use '.$database);
    }
    public function fetch ($query)
    {
        $result = $this->link->query($query)->fetch();
    }
    public function query ($query)
    {
        return $this->link->query($query);
    }
    public function error ()
    {
        return $this->link->errorInfo();
    }
}

//This class collects table repositories and connections
class database
{
    public $link;
    public $tables = array ();
    public function __construct ($link)
    {
        $this->link = $link;
        table::$database = $this;
    }
}

//This is basic table repositor class
class table
{
    public static $database;
}

?>

Now as we have our db framework let us make some table repositor which knows how to save/load/delete game: 现在我们有了db框架,让我们制作一些知道如何保存/加载/删除游戏的表格存储库:

class games extends table
{
    public function create ($row)
    {
        $return = new game ();
        $return->id = $row[0];
        $return->name = $row[1];
        var_export($row);
        return $return;
    }
    public function load ($id=null)
    {
        if ($id==null)
        {
            $result = self::$database->link->fetch("select * from games");
            if ($result)
            {
                $return = array();
                foreach ($result as $row)
                {
                    $return[$row[0]] = $this->create($row);
                }
                return $return;
            }
        }
        else
        {
            $result = self::$database->link->fetch("select * from games where id='".$id."'");
            if ($result)
            {
                return $this->create(reset($result));
            }
            else
            {
                echo ("no result");
            }
        }
    }
    public function save ($game)
    {
        if (is_array($save))
        {
            foreach ($save as $item) $this->save ($item);
        }
        if ($game->id==null)
        {
            return self::$database->link->query("insert into games set
                                                 name='".$game->name."'");
        }
        else
        {
            return self::$database->link->query("update games set name='".$game->name."'
                                                 where id='".$game->id."'");
        }
    }
    public function delete ($game)
    {
        self::$database->link->query ("delete from games where id='".$game->id."'");
    }
}

Now we can make our model which in this case will contain actuall game class. 现在我们可以制作我们的模型,在这种情况下将包含实际的游戏类。

class game
{
    public $id;
    public $name;
    public function __construct ($name=null)
    {
        $this->name = $name;
    }
}

And than actually use it: 而实际使用它:

$database = new database (new link('127.0.0.1', 'system_db', 'root', '1234'));
$database->tables['games'] = new games();

if (!$database->tables['games']->save (new game('Admin')))
{
    var_export($database->link->error());
}

var_export($database->tables['games']->load(2));

For the moment I prefere this pattern for working with db in my projects. 目前,我喜欢这种模式,以便在我的项目中使用db。 Using it I can achieve that my actuall business objects(In this case class game) will know nothing about where and how they are saved. 使用它我可以实现我的实际业务对象(在这种情况下类游戏)将不知道它们的保存位置和方式。 This gives me an ability to be indipendent from actuall storage and focus on project logics. 这使我能够独立于实际存储并专注于项目逻辑。

Also there is one lightweight framework so called db.php ( http://dbphp.net ) and it even gives me ability to avoid to write table repositories and even creates/modifies tables needed for my business classes on the fly but uses almost same concept I described here. 还有一个轻量级框架,所谓的db.php( http://dbphp.net ),它甚至让我能够避免编写表存储库,甚至创建/修改我的业务类所需的表,但使用几乎相同我在这里描述的概念。

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

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