Zend Framework 2 - 使用Memcache / Memcached存储DB结果

时间:2015-06-26 17:23:54

标签: php zend-framework2 memcached

我尝试实现memcache来存储Db结果(在tablegateway中),但是我遇到了问题。没有“setItem”和“getItem”方法,查询工作正常,但如果我使用它们,它会显示此错误:

An error occurred

An error occurred during execution; please try again later.
Additional information:
PDOException
File:
C:\Program Files (x86)\xampp\htdocs\ZF-Tutorial\vendor\zendframework\zendframework\library\Zend\Serializer\Adapter\PhpSerialize.php:48
Message:
You cannot serialize or unserialize PDOStatement instances

在我的tablegateway里面有一个

namespace Application\Model;

use Zend\Cache\Storage\StorageInterface;
use Zend\Db\TableGateway\TableGateway;
use Zend\Db\Sql\Sql;
use Application\Model\Defaultlanguage;

class AccessoriesTable 
{
    protected $tableGateway;
    protected $cache;

public function __construct(TableGateway $tableGateway)
{
    $this->tableGateway = $tableGateway;
}


public function setCache(StorageInterface $cache)
{
    $this->cache = $cache;
} 


public function fetchAll($lang = null)
{
    if(!isset($lang))  {  
        $lang = DefaultLanguage::LANG;
    }

    if( ($result = $this->cache->getItem('testcache')) == FALSE) {
        $adapter = $this->tableGateway->getAdapter();
        $sql = new Sql($adapter);
        $select = $sql->select();
        $select->columns(array(
            'id',
            'accessories' => $lang
        ));
        $select->from('cat_accessories'); 
        $select->order('accessories ASC');
        $statement = $sql->prepareStatementForSqlObject($select);
        $result = $statement->execute();

        $this->cache->setItem('testcache',  $result);
    }
    return $result; 
}

在我的module.php

'Application\Model\AccessoriesTable' =>  function($sm) {
    $tableGateway = $sm->get('AccessoriesTableGateway');
    $cacheAdapter = $sm->get('Zend\Cache\Storage\Memcache');
    $table = new AccessoriesTable($tableGateway);
    $table->setCache($cacheAdapter);
    return $table;
},
'AccessoriesTableGateway' => function ($sm) {
    $dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
    $resultSetPrototype = new ResultSet();
    $resultSetPrototype->setArrayObjectPrototype(new Accessories());
    return new TableGateway('cat_accessories', $dbAdapter, null, $resultSetPrototype);
},

我尝试在我的控制器中使用memcache并查看并正常工作。我不明白错误在哪里。谢谢大家的帮助。

编辑28-06-2015

我在这里找到了一个解决方案,工作正常,但我不喜欢这么多: https://samsonasik.wordpress.com/2012/09/27/zend-framework-2-using-zendcache-and-hydratingresultset-to-save-database-resultset/

在module.php里面

'Application\Model\AccessoriesTable' =>  function($sm) {
    $dbAdapter    = $sm->get('Zend\Db\Adapter\Adapter');
    $cacheAdapter = $sm->get('Zend\Cache\Storage\Memcache');
    $table = new AccessoriesTable($dbAdapter);
    $table->setCache($cacheAdapter);
    return $table;
},

内部配件类:

namespace Application\Model;

class Accessories 
{
    public $id;
    public $accessories;

    public function exchangeArray($data)
    {
        $this->id     = (!empty($data['id'])) ? $data['id'] : null;
        $this->accessories     = (!empty($data['accessories'])) ? $data['accessories'] : null;
    }   

    public function getArrayCopy()
    {
        return get_object_vars($this);
    }
}

Inside AccessoriesTable:

namespace Application\Model;

use Zend\Db\Adapter\Adapter;
use Zend\Db\ResultSet\HydratingResultSet;
use Zend\Db\TableGateway\AbstractTableGateway;
use Zend\Db\Sql\Select;
use Zend\Cache\Storage\StorageInterface;
use Application\Model\Accessories;
use Application\Model\Defaultlanguage;

class AccessoriesTable extends AbstractTableGateway
{
    protected $table = 'cat_accessories';
    protected $cache;
    public $lang;

    public function __construct(Adapter $adapter)
    {
        $this->adapter = $adapter;
        $this->resultSetPrototype = new HydratingResultSet();
        $this->resultSetPrototype->setObjectPrototype(new Accessories());
        $this->initialize();
    }

    public function fetchAll($lang = null)
    {
        $this->setLang($lang);
        $cacheName = md5('accessories-'.$this->lang);
        if( ($resultSet = $this->cache->getItem($cacheName)) == FALSE) {
            $resultSet = $this->select(function (Select $select){
                $select->columns(array('id', 'accessories'=> $this->lang ));
                $select->order(array($this->lang. ' asc'));  
            });
            $this->cache->setItem($cacheName ,  $resultSet );
        }
        return $resultSet; 
    }
}

新问题:如何使用“传统”tablegateway构造实现它????

我尝试使用传统的tablegateway,但上面的错误是一样的。

在tablegateway里面

public function fetchAll($lang = null)
{
    $this->setLang($lang);
    $cacheName = md5('accessories-'.$this->lang);
    if( ($resultSet = $this->cache->getItem($cacheName)) == FALSE) {
        $resultSet = $this->tableGateway->select(function (Select $select){
            $select->columns(array('id', 'accessories'=> $this->lang ));
            $select->order(array($this->lang. ' asc'));  
        });
        $this->cache->setItem($cacheName ,  $resultSet );
    }
    return $resultSet; 
}

2 个答案:

答案 0 :(得分:0)

这些是错误:

$result = $statement->execute();
$this->cache->setItem('testcache',  $result);

您的$ result变量是一个ResultSet对象,您试图在memcached中插入该对象(整个对象,而不仅仅是它的返回数据)。尝试这样做$result->current();并让我知道发生了什么。

编辑:28.06.2015

由于我给你的链接将在一周后过期,我决定将代码包含在

/**
 * Create plain mysql queries.
 *
 * @param String $sql the plain query
 * @throws Exception If database is not found or $sql is empty
 * @return array|HydratingResultSet|null
 */
use Zend\Db\Adapter\Adapter;
use Zend\Db\ResultSet\HydratingResultSet;
use Zend\Stdlib\Hydrator\ObjectProperty;
use Zend\Db\Adapter\Driver\Pdo\Result;

public static function createPlainQuery($sql = null)
{
    $dir = dirname(dirname(dirname(__DIR__)));

    if (!is_file($dir.'/config/autoload/local.php')) {
        throw new \Exception("Could not load database settings");
    }

    if (empty($sql)) {
        throw new \Exception(__METHOD__ . ' must not be empty');
    }

    $local = require($dir.'/config/autoload/local.php');
    $db = new Adapter($local['db']);
    $stmt = $db->createStatement((string) $sql);
    $stmt->prepare();
    $result = $stmt->execute();
    if ($result instanceof Result && $result->isQueryResult() && $result->getAffectedRows()) {
        $resultSet = new HydratingResultSet(new ObjectProperty(), new \stdClass());
        $resultSet->initialize($result);
        $resultSet->buffer();

        return ($resultSet->valid() && $resultSet->count() > 0 ? $resultSet->toArray() : null);
    }
    return null;
}

答案 1 :(得分:0)

在第一条错误消息中明确指出的主要问题;您正在尝试保存PDOStatement实例,该实例是execute()方法的输出。然后你的缓存后端会自动尝试序列化那个实际上不可序列化的语句实例。

$result = $statement->execute();
$this->cache->setItem('testcache',  $result);

从结果集中获取数据并迭代它。在迭代时将数据转换为数组,或者如果项是对象,则通过实现ArraySerializableInterface向这些对象添加getArrayCopy()方法,以轻松提取对象的数组副本并存储到缓存中。

对于大多数情况,在getArrayCopy()方法中返回一个简单的关联数组就足够了,例如:

public function getArrayCopy()
{
   return [
       'id' => $this->id,
       'name' => $this->name,
       'bar' => $this->bar,
       'baz' => $this->baz,
   ];
}