Skip to content

Commit 330e1e3

Browse files
committed
PHPLIB-138: Support typeMap option for Database::command()
This refactors the existing Database method to use an Operation class.
1 parent a3bf702 commit 330e1e3

File tree

4 files changed

+156
-18
lines changed

4 files changed

+156
-18
lines changed

src/Database.php

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
namespace MongoDB;
44

55
use MongoDB\Collection;
6-
use MongoDB\Driver\Command;
76
use MongoDB\Driver\Cursor;
87
use MongoDB\Driver\Manager;
98
use MongoDB\Driver\Query;
@@ -15,6 +14,7 @@
1514
use MongoDB\Exception\InvalidArgumentTypeException;
1615
use MongoDB\Model\CollectionInfoIterator;
1716
use MongoDB\Operation\CreateCollection;
17+
use MongoDB\Operation\DatabaseCommand;
1818
use MongoDB\Operation\DropCollection;
1919
use MongoDB\Operation\DropDatabase;
2020
use MongoDB\Operation\ListCollections;
@@ -116,27 +116,22 @@ public function __toString()
116116
/**
117117
* Execute a command on this database.
118118
*
119-
* @param array|object $command Command document
120-
* @param ReadPreference|null $readPreference Read preference
119+
* @see DatabaseCommand::__construct() for supported options
120+
* @param array|object $command Command document
121+
* @param array $options Options for command execution
121122
* @return Cursor
123+
* @throws InvalidArgumentException
122124
*/
123-
public function command($command, ReadPreference $readPreference = null)
125+
public function command($command, array $options = [])
124126
{
125-
if ( ! is_array($command) && ! is_object($command)) {
126-
throw new InvalidArgumentTypeException('$command', $command, 'array or object');
127-
}
128-
129-
if ( ! $command instanceof Command) {
130-
$command = new Command($command);
127+
if ( ! isset($options['readPreference'])) {
128+
$options['readPreference'] = $this->readPreference;
131129
}
132130

133-
if ( ! isset($readPreference)) {
134-
$readPreference = $this->readPreference;
135-
}
136-
137-
$server = $this->manager->selectServer($readPreference);
131+
$operation = new DatabaseCommand($this->databaseName, $command, $options);
132+
$server = $this->manager->selectServer($options['readPreference']);
138133

139-
return $server->executeCommand($this->databaseName, $command);
134+
return $operation->execute($server);
140135
}
141136

142137
/**

src/Operation/DatabaseCommand.php

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
namespace MongoDB\Operation;
4+
5+
use MongoDB\Driver\Command;
6+
use MongoDB\Driver\ReadPreference;
7+
use MongoDB\Driver\Server;
8+
use MongoDB\Exception\InvalidArgumentException;
9+
use MongoDB\Exception\InvalidArgumentTypeException;
10+
11+
/**
12+
* Operation for executing a database command.
13+
*
14+
* @api
15+
* @see MongoDB\Database::command()
16+
*/
17+
class DatabaseCommand implements Executable
18+
{
19+
private $databaseName;
20+
private $command;
21+
private $options;
22+
23+
/**
24+
* Constructs a command.
25+
*
26+
* Supported options:
27+
*
28+
* * readPreference (MongoDB\Driver\ReadPreference): The read preference to
29+
* use when executing the command. This may be used when issuing the
30+
* command to a replica set or mongos node to ensure that the driver sets
31+
* the wire protocol accordingly or adds the read preference to the
32+
* command document, respectively.
33+
*
34+
* * typeMap (array): Type map for BSON deserialization. This will be
35+
* applied to the returned Cursor (it is not sent to the server).
36+
*
37+
* @param string $databaseName Database name
38+
* @param array|object $command Command document
39+
* @param array $options Options for command execution
40+
* @throws InvalidArgumentException
41+
*/
42+
public function __construct($databaseName, $command, array $options = [])
43+
{
44+
if ( ! is_array($command) && ! is_object($command)) {
45+
throw new InvalidArgumentTypeException('$command', $command, 'array or object');
46+
}
47+
48+
if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) {
49+
throw new InvalidArgumentTypeException('"readPreference" option', $options['readPreference'], 'MongoDB\Driver\ReadPreference');
50+
}
51+
52+
if (isset($options['typeMap']) && ! is_array($options['typeMap'])) {
53+
throw new InvalidArgumentTypeException('"typeMap" option', $options['typeMap'], 'array');
54+
}
55+
56+
$this->databaseName = (string) $databaseName;
57+
$this->command = ($command instanceof Command) ? $command : new Command($command);
58+
$this->options = $options;
59+
}
60+
61+
/**
62+
* Execute the operation.
63+
*
64+
* @see Executable::execute()
65+
* @param Server $server
66+
* @return integer
67+
*/
68+
public function execute(Server $server)
69+
{
70+
$readPreference = isset($this->options['readPreference']) ? $this->options['readPreference'] : null;
71+
72+
$cursor = $server->executeCommand($this->databaseName, $this->command, $readPreference);
73+
74+
if (isset($this->options['typeMap'])) {
75+
$cursor->setTypeMap($this->options['typeMap']);
76+
}
77+
78+
return $cursor;
79+
}
80+
}

tests/Database/DatabaseFunctionalTest.php

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,11 @@ public function getGetDatabaseName()
6868
public function testCommand()
6969
{
7070
$command = ['isMaster' => 1];
71-
$readPreference = new ReadPreference(ReadPreference::RP_PRIMARY);
72-
$cursor = $this->database->command($command, $readPreference);
71+
$options = [
72+
'readPreference' => new ReadPreference(ReadPreference::RP_PRIMARY),
73+
];
74+
75+
$cursor = $this->database->command($command, $options);
7376

7477
$this->assertInstanceOf('MongoDB\Driver\Cursor', $cursor);
7578
$commandResult = current($cursor->toArray());
@@ -79,6 +82,25 @@ public function testCommand()
7982
$this->assertTrue($commandResult->ismaster);
8083
}
8184

85+
public function testCommandAppliesTypeMapToCursor()
86+
{
87+
$command = ['isMaster' => 1];
88+
$options = [
89+
'readPreference' => new ReadPreference(ReadPreference::RP_PRIMARY),
90+
'typeMap' => ['root' => 'array'],
91+
];
92+
93+
$cursor = $this->database->command($command, $options);
94+
95+
$this->assertInstanceOf('MongoDB\Driver\Cursor', $cursor);
96+
$commandResult = current($cursor->toArray());
97+
98+
$this->assertCommandSucceeded($commandResult);
99+
$this->assertInternalType('array', $commandResult);
100+
$this->assertTrue(isset($commandResult['ismaster']));
101+
$this->assertTrue($commandResult['ismaster']);
102+
}
103+
82104
/**
83105
* @expectedException MongoDB\Exception\InvalidArgumentTypeException
84106
* @dataProvider provideInvalidDocumentValues
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
namespace MongoDB\Tests\Operation;
4+
5+
use MongoDB\Operation\DatabaseCommand;
6+
7+
class DatabaseCommandTest extends TestCase
8+
{
9+
/**
10+
* @expectedException MongoDB\Exception\InvalidArgumentTypeException
11+
* @dataProvider provideInvalidDocumentValues
12+
*/
13+
public function testConstructorCommandArgumentTypeCheck($command)
14+
{
15+
new DatabaseCommand($this->getDatabaseName(), $command);
16+
}
17+
18+
/**
19+
* @expectedException MongoDB\Exception\InvalidArgumentTypeException
20+
* @dataProvider provideInvalidConstructorOptions
21+
*/
22+
public function testConstructorOptionTypeChecks(array $options)
23+
{
24+
new DatabaseCommand($this->getDatabaseName(), ['ping' => 1], $options);
25+
}
26+
27+
public function provideInvalidConstructorOptions()
28+
{
29+
$options = [];
30+
31+
foreach ($this->getInvalidReadPreferenceValues() as $value) {
32+
$options[][] = ['readPreference' => $value];
33+
}
34+
35+
foreach ($this->getInvalidArrayValues() as $value) {
36+
$options[][] = ['typeMap' => $value];
37+
}
38+
39+
return $options;
40+
}
41+
}

0 commit comments

Comments
 (0)