From 4ae1a29a727643b729b0a14df397c6a5b0e0ce23 Mon Sep 17 00:00:00 2001 From: imsamurai Date: Tue, 29 Oct 2013 21:53:19 +0200 Subject: [PATCH] add HABTM association handle, write tests, see #18 --- Model/Datasource/HttpSource.php | 58 +++++++++++++++ .../TestHttpSource/Config/TestHttpSource.php | 44 +++++++++++ .../Model/Datasource/Http/TestHttpSource.php | 31 ++++++++ .../Datasource/TestHttpSourceConnection.php | 27 +++++++ .../Model/TestHttpSourceModel.php | 38 ++++++++++ Test/Case/AllHttpSourceTest.php | 23 ++++++ Test/Case/Model/BasicsTest.php | 34 +++++++++ Test/Case/Model/ToDboAssociationTest.php | 49 ++++++++++++ Test/Data/models.php | 31 ++++++++ Test/Fixture/UserFixture.php | 32 ++++++++ Test/Fixture/UsersDocumentFixture.php | 33 +++++++++ Test/HttpSourceTest.php | 74 +++++++++++++++++++ 12 files changed, 474 insertions(+) create mode 100644 Plugin/TestHttpSource/Config/TestHttpSource.php create mode 100644 Plugin/TestHttpSource/Model/Datasource/Http/TestHttpSource.php create mode 100644 Plugin/TestHttpSource/Model/Datasource/TestHttpSourceConnection.php create mode 100644 Plugin/TestHttpSource/Model/TestHttpSourceModel.php create mode 100644 Test/Case/AllHttpSourceTest.php create mode 100644 Test/Case/Model/BasicsTest.php create mode 100644 Test/Case/Model/ToDboAssociationTest.php create mode 100644 Test/Data/models.php create mode 100644 Test/Fixture/UserFixture.php create mode 100644 Test/Fixture/UsersDocumentFixture.php create mode 100644 Test/HttpSourceTest.php diff --git a/Model/Datasource/HttpSource.php b/Model/Datasource/HttpSource.php index 64e9df0..e07cf65 100644 --- a/Model/Datasource/HttpSource.php +++ b/Model/Datasource/HttpSource.php @@ -546,6 +546,64 @@ public function getQueryCache(array $request) { return false; } + /** + * Queries associations. Used to fetch results on recursive models. + * + * @param Model $model Primary Model object + * @param Model $linkModel Linked model that + * @param string $type Association type, one of the model association types ie. hasMany + * @param string $association + * @param array $assocData + * @param array $queryData + * @param boolean $external Whether or not the association query is on an external datasource. + * @param array $resultSet Existing results + * @param integer $recursive Number of levels of association + * @param array $stack + * @return mixed + * @throws CakeException when results cannot be created. + */ + public function queryAssociation(Model $model, &$linkModel, $type, $association, $assocData, &$queryData, $external, &$resultSet, $recursive, $stack) { + $assocQuery = $this->_scrubQueryData($assocData); + $query = $this->_scrubQueryData($queryData); + + foreach ($resultSet as &$result) { + switch($type) { + case 'hasAndBelongsToMany': { + $JoinModel = ClassRegistry::init($assocData['with']); + $assocQuery['fields'] = array($assocData['associationForeignKey']); + $assocQuery['conditions'][$assocData['foreignKey']] = $result[$model->alias][$model->primaryKey]; + $joinData = $JoinModel->find('all', array_filter($assocQuery)); + $query['conditions'][$linkModel->primaryKey] = Hash::extract($joinData, "{n}.{$JoinModel->alias}.{$assocData['associationForeignKey']}"); + $assocResults = $linkModel->find('all', $query); + + $result[$association] = array(); + foreach ($assocResults as $assocResult) { + $result[$association][] = $assocResult[$linkModel->alias]; + } + + break; + } + + default: throw new NotImplementedException("Sorry, but type $type is not implemented yet, check out https://github.com/imsamurai/cakephp-httpsource-datasource/issues/18"); + } + } + } + + /** + * Private helper method to remove query metadata in given data array. + * + * @param array $data + * @return array + */ + protected function _scrubQueryData($data) { + static $base = null; + if ($base === null) { + $base = array_fill_keys(array('conditions', 'fields', 'joins', 'order', 'limit', 'offset', 'group'), array()); + $base['callbacks'] = null; + } + return (array) $data + $base; + } + /** * Format result to match Cake conventions * diff --git a/Plugin/TestHttpSource/Config/TestHttpSource.php b/Plugin/TestHttpSource/Config/TestHttpSource.php new file mode 100644 index 0000000..65a32f8 --- /dev/null +++ b/Plugin/TestHttpSource/Config/TestHttpSource.php @@ -0,0 +1,44 @@ + + * Date: Oct 29, 2013 + * Time: 5:54:32 PM + * + */ +$config['TestHttpSource']['config_version'] = 2; + +$CF = HttpSourceConfigFactory::instance(); +$Config = $CF->config(); + +$Config + ->add( + $CF->endpoint() + ->id(1) + ->methodRead() + ->table('default') + ->path('/imsamurai/cakephp-httpsource-datasource/master/Test/Data/default.json') + ->result($CF->result()->map(function($result) { + return array($result); + })) + ) + ->add( + $CF->endpoint() + ->id(2) + ->methodRead() + ->table('documents') + ->path('/imsamurai/cakephp-httpsource-datasource/master/Test/Data/documents:id.json') + ->addCondition($CF->condition()->name('id')->map(function($v) { + return implode('.', $v); + })) + ->result($CF->result()->map(function($result) { + return $result; + })) + ) + +; + +$config['TestHttpSource']['config'] = $Config; + + +$config['TestHttpSource']['cache_name'] = false; diff --git a/Plugin/TestHttpSource/Model/Datasource/Http/TestHttpSource.php b/Plugin/TestHttpSource/Model/Datasource/Http/TestHttpSource.php new file mode 100644 index 0000000..82b55be --- /dev/null +++ b/Plugin/TestHttpSource/Model/Datasource/Http/TestHttpSource.php @@ -0,0 +1,31 @@ + + * Date: Oct 29, 2013 + * Time: 5:44:49 PM + * + */ + +App::uses('HttpSource', 'HttpSource.Model/Datasource'); +App::uses('TestHttpSourceConnection', 'TestHttpSource.Model/Datasource'); + +class TestHttpSource extends HttpSource { + + /** + * The description of this data source + * + * @var string + */ + public $description = 'Test DataSource'; + + /** + * {@inheritdoc} + * + * @param array $config + * @param HttpSourceConnection $Connection + */ + public function __construct($config = array(), HttpSourceConnection $Connection = null) { + parent::__construct($config, new TestHttpSourceConnection($config)); + } +} diff --git a/Plugin/TestHttpSource/Model/Datasource/TestHttpSourceConnection.php b/Plugin/TestHttpSource/Model/Datasource/TestHttpSourceConnection.php new file mode 100644 index 0000000..74b3764 --- /dev/null +++ b/Plugin/TestHttpSource/Model/Datasource/TestHttpSourceConnection.php @@ -0,0 +1,27 @@ + + * Date: Oct 29, 2013 + * Time: 5:45:06 PM + * + */ +App::uses('HttpSourceConnection', 'HttpSource.Model/Datasource'); + +class TestHttpSourceConnection extends HttpSourceConnection { + + /** + * Constructor + * + * @param array $config + * @param HttpSocket $Transport + */ + public function __construct(array $config = array(), HttpSocket $Transport = null) { + parent::__construct($config, $Transport); + + $this->setDecoder(array('text/plain'), function(HttpSocketResponse $Response) { + return json_decode((string) $Response, true); + }, false); + } + +} diff --git a/Plugin/TestHttpSource/Model/TestHttpSourceModel.php b/Plugin/TestHttpSource/Model/TestHttpSourceModel.php new file mode 100644 index 0000000..04dcc06 --- /dev/null +++ b/Plugin/TestHttpSource/Model/TestHttpSourceModel.php @@ -0,0 +1,38 @@ + + * Date: Oct 29, 2013 + * Time: 6:02:17 PM + * Format: http://book.cakephp.org/2.0/en/models.html + */ + +App::uses('HttpSourceModel', 'HttpSource.Model'); + +/** + * TestHttpSourceModel Model + */ +class TestHttpSourceModel extends HttpSourceModel { + + /** + * {@inheritdoc} + * + * @var string + */ + public $name = 'TestHttpSourceModel'; + + /** + * {@inheritdoc} + * + * @var string + */ + public $useTable = 'default'; + + /** + * {@inheritdoc} + * + * @var string + */ + public $useDbConfig = 'http'; + +} \ No newline at end of file diff --git a/Test/Case/AllHttpSourceTest.php b/Test/Case/AllHttpSourceTest.php new file mode 100644 index 0000000..fa4df21 --- /dev/null +++ b/Test/Case/AllHttpSourceTest.php @@ -0,0 +1,23 @@ + + * Date: 29.10.2013 + * Time: 21:50:00 + */ +class AllHttpSourceTest extends PHPUnit_Framework_TestSuite { + + /** + * Suite define the tests for this suite + * + * @return void + */ + public static function suite() { + $suite = new CakeTestSuite('All Http Source Tests'); + + $path = App::pluginPath('HttpSource') . 'Test' . DS . 'Case' . DS; + $suite->addTestDirectoryRecursive($path); + return $suite; + } + +} diff --git a/Test/Case/Model/BasicsTest.php b/Test/Case/Model/BasicsTest.php new file mode 100644 index 0000000..d8b5176 --- /dev/null +++ b/Test/Case/Model/BasicsTest.php @@ -0,0 +1,34 @@ + + * Date: Oct 29, 2013 + * Time: 6:06:31 PM + * Format: http://book.cakephp.org/2.0/en/development/testing.html + */ +App::uses('HttpSourceTest', 'HttpSource.Test'); + +/** + * BasicsTest + */ +class BasicsTest extends HttpSourceTest { + + public function testRead() { + $this->HttpModel->setSource('default'); + $result = $this->HttpModel->field('name'); + + $this->assertSame('imsamurai', $result); + } + + public function testRead2() { + $this->HttpModel->setSource('documents'); + $result = $this->HttpModel->find('all', array( + 'conditions' => array( + 'id' => array(1,2) + ) + )); + + $this->assertCount(2, $result); + } + +} diff --git a/Test/Case/Model/ToDboAssociationTest.php b/Test/Case/Model/ToDboAssociationTest.php new file mode 100644 index 0000000..53a1f23 --- /dev/null +++ b/Test/Case/Model/ToDboAssociationTest.php @@ -0,0 +1,49 @@ + + * Date: Oct 29, 2013 + * Time: 5:28:22 PM + * Format: http://book.cakephp.org/2.0/en/development/testing.html + */ + +App::uses('HttpSourceTest', 'HttpSource.Test'); + +/** + * HttpSourceToDboAssociationTest + */ +class ToDboAssociationTest extends HttpSourceTest { + + /** + * User model + * + * @var User + */ + public $User = null; + /** + * {@inheritdoc} + */ + public function setUp(){ + parent::setUp(); + $this->User = new User(); + } + + /** + * {@inheritdoc} + * + * @var array + */ + public $fixtures = array( + 'plugin.HttpSource.User', + 'plugin.HttpSource.UsersDocument' + ); + + public function testHABTM() { + $this->User->Documents->useDbConfig = 'testHttpSource'; + $results = $this->User->find('all', array('recursive' => 3)); + debug($results); + foreach ($results as $result) { + $this->assertNotEmpty($result['Documents']); + } + } +} \ No newline at end of file diff --git a/Test/Data/models.php b/Test/Data/models.php new file mode 100644 index 0000000..9682d28 --- /dev/null +++ b/Test/Data/models.php @@ -0,0 +1,31 @@ + + * Date: Oct 29, 2013 + * Time: 9:18:11 PM + * Format: http://book.cakephp.org/2.0/en/views.html + * + */ +App::uses('HttpSourceModel', 'HttpSource.Model'); + +class Document extends HttpSourceModel { + + public $name = 'Document'; + public $useTable = 'documents'; + +} + +class User extends AppModel { + + public $name = 'User'; + public $useTable = 'users'; + public $useDbConfig = 'test'; + public $hasAndBelongsToMany = array( + 'Documents' => array( + 'joinTable' => 'users_documents', + 'dependent' => false + ) + ); + +} diff --git a/Test/Fixture/UserFixture.php b/Test/Fixture/UserFixture.php new file mode 100644 index 0000000..4e5006c --- /dev/null +++ b/Test/Fixture/UserFixture.php @@ -0,0 +1,32 @@ + array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 20, 'key' => 'primary'), + 'name' => array('type' => 'string', 'null' => false, 'length' => 100), + 'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_general_ci', 'engine' => 'MyISAM') + ); + + /** + * Records + * + * @var array + */ + public $records = array( + array('id' => 1, 'name' => 'dan'), + array('id' => 2, 'name' => 'robert') + ); + +} diff --git a/Test/Fixture/UsersDocumentFixture.php b/Test/Fixture/UsersDocumentFixture.php new file mode 100644 index 0000000..40190ac --- /dev/null +++ b/Test/Fixture/UsersDocumentFixture.php @@ -0,0 +1,33 @@ + array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 20), + 'document_id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 20), + 'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_general_ci', 'engine' => 'MyISAM') + ); + + /** + * Records + * + * @var array + */ + public $records = array( + array('user_id' => 1, 'document_id' => 1), + array('user_id' => 2, 'document_id' => 1), + array('user_id' => 2, 'document_id' => 2) + ); + +} diff --git a/Test/HttpSourceTest.php b/Test/HttpSourceTest.php new file mode 100644 index 0000000..3f32148 --- /dev/null +++ b/Test/HttpSourceTest.php @@ -0,0 +1,74 @@ + + * Date: 29.10.2013 + * Time: 18:00:00 + * Format: http://book.cakephp.org/2.0/en/development/testing.html + * + */ +App::uses('ConnectionManager', 'Model'); +App::uses('TestHttpSourceModel', 'TestHttpSource.Model'); + +require_once dirname(__FILE__) . DS . 'Data' . DS . 'models.php'; + +/** + * Tests + */ +abstract class HttpSourceTest extends CakeTestCase { + + /** + * TestHttpSourceModel Model + * + * @var TestHttpSourceModel + */ + public $HttpModel = null; + + /** + * {@inheritdoc} + * + * @param string $name + * @param array $data + * @param string $dataName + */ + public function __construct($name = NULL, array $data = array(), $dataName = '') { + $this->_loadModel(); + parent::__construct($name, $data, $dataName); + } + + /** + * {@inheritdoc} + */ + public function setUp() { + parent::setUp(); + $this->_loadModel(); + } + + /** + * Load model + * + * @param array $config_name + * @param array $config + */ + protected function _loadModel($config_name = 'testHttpSource', $config = array()) { + $db_configs = ConnectionManager::enumConnectionObjects(); + + if (!empty($db_configs['httpsourceTest'])) { + $TestDS = ConnectionManager::getDataSource('httpsourceTest'); + $config += $TestDS->config; + } else { + $config += array( + 'datasource' => 'TestHttpSource.Http/TestHttpSource', + 'host' => 'raw.github.com', + 'port' => 443, + 'timeout' => 5 + ); + } + + $config+=array('prefix' => ''); + + ConnectionManager::create($config_name, $config); + $this->HttpModel = new TestHttpSourceModel(false, null, $config_name); + } + +}