简体   繁体   English

与PDO :: FETCH_CLASS相反

[英]Opposite To PDO::FETCH_CLASS

So I've used PDO::FETCH_CLASS to make a new object, filling the properties with values from the columns of the same name in the database along the lines of: 因此,我使用了PDO :: FETCH_CLASS来创建一个新的对象,并使用数据库中同名列中的值填充属性,包括:

$db = Connection::get();
$sql = $db->prepare("SELECT * FROM table WHERE id = :id");
$sql->bindParam(":id", "1");
$sql->setFetchMode(PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE, 'table');
$sql->execute();
$newobj = $sql->fetch();

Is there an opposite for inserting an object's properties into their corresponding columns in the table, to save typing a whole load of bindParam() and a long SQL query? 将对象的属性插入表中的相应列中以节省键入整个bindParam()负载和较长的SQL查询是否有相反的说法?

Many thanks 非常感谢

this was a fun one to do 这很有趣

  • One caveat is this will take all the public properties in the class and try to use them as part of the query. 需要注意的是,这将占用类中的所有public属性,并尝试将其用作查询的一部分。 You could filter them more if you really need to. 如果确实需要,可以对它们进行更多过滤。

We are going to be using what is known as reflection. 我们将使用所谓的反射。

This is sort of a double edged sword. 这是一把双刃剑。

On the one hand we can put this in a base class and extend it with all your objects and it would work just fine for them. 一方面,我们可以将其放在基类中,并用您的所有对象扩展它,这对他们来说将很好用。 If at a later date you add a field to the table, same deal you don't have to change anything, it just works. 如果以后再将一个字段添加到表中,则无需更改任何内容,它就可以正常工作。

On the other hand it takes a small amount of time to process this instead of say having it written as a string. 另一方面,处理此问题要花费少量时间,而不是说将其编写为字符串。 Some of this could be addressed by caching the queries inside protected static properties and by caching the properties themselves ( but not the values ) 可以通过在protected static properties缓存查询和缓存属性本身(而不是值)来解决其中的某些问题

I will start off by making an Abstract Class. 我将从创建一个抽象类开始。 This is a very flexible approach to this problem and it lends it self to being reused - Note: I tested only the Insert method so forgive me if there are any errors in the other parts. 这是解决此问题的非常灵活的方法,它很容易被重用-注意:我仅测试了Insert方法,因此如果其他部分中没有任何错误,请原谅。

  abstract class BaseObject
  {

    public $id;
    protected static $_DB;

   public function getDB(\PDO $db ){
      if( !self::$_DB ){
          //@todo: connect to Database
      }
      return self::$_DB;
   }

    public function getId(){ return $this->id; }

    public function setId( $id ){ $this->id = $id }

    //returns the records id
    public function save(){
        //if there is no ID then we know it didn't come from the DB
        if( $this->id ) 
            return $this->update();
        else
            return $this->insert();
    }

    // query format = 'INSERT INTO table (id, ... )VALUES(:id, ... )'
    public function insert(){
        $this->validate();

        $db = $this->getDB(); //localize

        $R = new \ReflectionObject( $this );
        $props = (array)$R->getProperties()[0];

        $names = array_keys( $props );

        $sql = 'INSERT INTO '.$this->getTable().' ('.implode(',', $names).' )VALUES( :'.implode(', :', $names).' )';
        $params = array_combine(
            array_map(function($item){
                return ':'.$item;
            }, $names),
            $props
         );

        $stmt =  $db ->prepare( $sql );
        $stmt->execute( $params );

        $this->id = $db->lastInsertId(); //don't forget to update the id
        return $this->id;
    }

    // query format = 'UPDATE table SET prop=:prop, ... WHERE id=:id'
    public function Update(){
        $this->validate();

        $db = $this->getDB(); //localize

        $R = new \ReflectionObject( $this );
        $props = (array)$R->getProperties()[0];
        $names = array_keys( $props );

        $sql = 'UPATE '.$this->getTable().' SET ';
        $set = [];
        $params = [];
        foreach( $props as $name=>$value ){     
            $params[':'.$name] = $value;

            if( $name == 'id' ) continue;
            $set[] = "$name = :$name";
        }

        $sql .= implode(', ', $set).' WHERE id=:id'


        $stmt = $db->prepare( $sql );
        $stmt->execute( $params );

        return $this->_id;
    }

    abstract public function getTable();

    abstract public function vallidate();

  }

Then in your concrete classes you just need to implement the abstract methods, and add the other properties specific to them 然后,在具体的类中,您只需要实现抽象方法,并添加特定于它们的其他属性

  class Dude extends BaseObject
  {
      public $name;

      public function getName(){ return $this->name ; }

      public function setName( $name ){ $this->name = $name }

      public function getTable(){
           return 'dudes';
      }

     public function validate(){
          if( empty( $this->name ) ) throw new \Exception( "Name cannot be empty" );

       //...etc.
     }
  }

One thing I happened to think of that you should be aware of, When loading a class -via- the PDO results, the class's constructor is not called. 我碰巧想到的一件事是,您应该知道,通过PDO结果加载类时,不会调用该类的构造函数。 It's been a while sense I loaded a class this way and there may be a way to force it to call the constructor, or I may be just recalling incorrectly. 我以这种方式加载类已经有一段时间了,也许有一种方法可以迫使它调用构造函数,或者我可能只是错误地调用了。 But it's something worth mentioning. 但这值得一提。

The reason I mention that is that the abstract class needs a PDO instance, so I went and added the getDB() method. 我提到的原因是抽象类需要一个PDO实例,因此我添加了getDB()方法。 This is just a brief example on how you can cache the DB connection for all the classes, ( I was to lazy to do the actual connection part, sorry ) 这只是一个有关如何为所有类缓存数据库连接的简短示例,(我懒于做实际的连接部分,对不起)

Personally I use what is called a Singleton for my DB needs so I would just call something like this self::$_DB = DB::getInstance(); 就我个人而言,我为数据库需求使用了称为Singleton的东西,因此我只称呼类似self::$_DB = DB::getInstance(); there but that's a story for another day. 在那里,那是另一天的故事。

I would also suggest adding a delete method, so you get that whole CRUD experience all your programmer fiends keep talking about. 我还建议添加一个delete方法,这样您就可以得到所有CRUD经验,您的程序员一直在谈论。

One other major improvement I can think of is you could store some of this stuff in static properties, basically cache it after the first time its ran. 我可以想到的另一项重大改进是,您可以将这些内容存储在静态属性中,基本上在第一次运行后对其进行缓存。 That would save some on re-processing ( introspection ) of the class multiple times. 这样可以节省多次对类的重新处理(自省)。

Its a bit tricky though and you would want to further break things down. 但是,这有点棘手,您需要进一步分解。 One tip I can give you on that is make sure to use static::$Var in the base class and not self::$Var so you use whats called Late Static Binding . 我可以给您的提示是,请确保在基类中使用static::$Var而不是self::$Var以便您使用称为Late Static Binding That's basically the tricky part, because you could have descendant classes with totally different stuff. 那基本上是棘手的部分,因为您的后代类可能具有完全不同的内容。 I'm not sure if this is the correct term but it's kind of a scope resolution problem. 我不确定这是否是正确的术语,但这是一个范围解析问题。

But I will leave these last things up to you. 但我会将这些最后的事情留给您。 This example should point you down the right path ( or at least a path I know works ), and give you some ideas of what is possible. 这个例子应该为您指明正确的道路(或者至少是我知道的一条道路),并给您一些可能的想法。

One last thing is it's perfectly fine to access properties or call methods using strings like this ( assuming they exist of course ) 最后一件事是使用这样的字符串访问属性或调用方法是完全可以的(假设它们当然存在)

foreach( $props as $name=>$value ){
     //for this we will say $name = 'id'
    $method = "get".ucFirst( $name ); // 'getId'
    $a = $this->$method(); // calls $this->getId()
    $a = $this->$name;  //access property $this->id; 
}

I just thought I would put that out there to give you another way to access the data that you might find useful. 我只是以为我可以在那儿给您提供另一种访问可能有用的数据的方法。

I think you want to do something like this. 我想你想做这样的事情。 refer (The only proper) PDO tutorial 参考(唯一合适的)PDO教程

$arr = [1,2,3];
$in  = str_repeat('?,', count($arr) - 1) . '?';
$sql = "SELECT * FROM table WHERE foo=? AND column IN ($in) AND bar=? AND baz=?";
$stm = $db->prepare($sql);
$params = array_merge([$foo], $arr, [$bar, $baz]);
$stm->execute($params);
$data = $stm->fetchAll();

this may give you better idea on this, if I have not understood your question properly please let me know. 如果您对我的问题没有正确的理解,请告诉我。

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

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