Skip to content

Commit bd152d9

Browse files
authored
Merge pull request #59 from xp-forge/feature/fluent-document
Add fields API to Document
2 parents fd291de + 835cc7b commit bd152d9

File tree

2 files changed

+199
-0
lines changed

2 files changed

+199
-0
lines changed

src/main/php/com/mongodb/Document.class.php

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,72 @@ public function offsetUnset($name) {
7979
/** Iterator over all properties */
8080
public function getIterator(): Traversable { yield from $this->properties; }
8181

82+
/**
83+
* Gets a field. Returns NULL if it did not exist.
84+
*
85+
* @param string $field
86+
* @return var
87+
*/
88+
public function get($field) {
89+
$ptr= &$this->properties;
90+
foreach (explode('.', $field) as $path) {
91+
$ptr= &$ptr[$path];
92+
}
93+
return $ptr;
94+
}
95+
96+
/**
97+
* Sets a field to a given value
98+
*
99+
* @param string $field
100+
* @param var $value
101+
* @return self
102+
*/
103+
public function with($field, $value) {
104+
$ptr= &$this->properties;
105+
foreach (explode('.', $field) as $path) {
106+
$ptr= &$ptr[$path];
107+
}
108+
$ptr= $value;
109+
return $this;
110+
}
111+
112+
/**
113+
* Updates this document from a given iterable producing field => value pairs.
114+
*
115+
* @param iterable $from
116+
* @return self
117+
*/
118+
public function update(iterable $from) {
119+
foreach ($from as $field => $value) {
120+
$ptr= &$this->properties;
121+
foreach (explode('.', $field) as $path) {
122+
$ptr= &$ptr[$path];
123+
}
124+
$ptr= $value;
125+
}
126+
return $this;
127+
}
128+
129+
/**
130+
* Unsets a list of fields
131+
*
132+
* @param string... $fields
133+
* @return self
134+
*/
135+
public function unset(... $fields) {
136+
foreach ($fields as $field) {
137+
$paths= explode('.', $field);
138+
$last= array_pop($paths);
139+
$ptr= &$this->properties;
140+
foreach ($paths as $path) {
141+
$ptr= &$ptr[$path];
142+
}
143+
unset($ptr[$last]);
144+
}
145+
return $this;
146+
}
147+
82148
/** @return string */
83149
public function hashCode() {
84150
return 'D'.Objects::hashOf($this->properties);

src/test/php/com/mongodb/unittest/DocumentTest.class.php

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ private function representations() {
1818
yield [['_id' => 6100, 'key' => 'value'], "com.mongodb.Document(6100)@{\n key: \"value\"\n}"];
1919
}
2020

21+
/** @return iterable */
22+
private function iterables() {
23+
yield [['topic' => 'Test']];
24+
yield [new Document(['topic' => 'Test'])];
25+
yield [(function() { yield 'topic' => 'Test'; })()];
26+
}
27+
2128
#[Test]
2229
public function can_create() {
2330
new Document();
@@ -115,6 +122,132 @@ public function set_offset_array_key() {
115122
Assert::equals(['color' => 'green', 'price' => 12.99], $fixture['properties']);
116123
}
117124

125+
#[Test]
126+
public function get() {
127+
$fixture= new Document(['topic' => 'Test', 'context' => ['readonly' => true]]);
128+
129+
Assert::equals('Test', $fixture->get('topic'));
130+
Assert::equals(true, $fixture->get('context.readonly'));
131+
}
132+
133+
#[Test]
134+
public function get_non_existant() {
135+
$fixture= new Document(['topic' => 'Test', 'context' => ['readonly' => true]]);
136+
137+
Assert::equals(null, $fixture->get('state'));
138+
Assert::equals(null, $fixture->get('context.owner'));
139+
}
140+
141+
#[Test, Values(from: 'iterables')]
142+
public function update_from($iterable) {
143+
$fixture= new Document(['id' => 'one']);
144+
145+
Assert::equals(
146+
['id' => 'one', 'topic' => 'Test'],
147+
$fixture->update($iterable)->properties()
148+
);
149+
}
150+
151+
#[Test]
152+
public function add_field_via_update() {
153+
$fixture= new Document(['id' => 'one']);
154+
155+
Assert::equals(
156+
['id' => 'one', 'topic' => 'Test'],
157+
$fixture->update(['topic' => 'Test'])->properties()
158+
);
159+
}
160+
161+
#[Test]
162+
public function change_field_via_update() {
163+
$fixture= new Document(['id' => 'one', 'topic' => 'Test']);
164+
165+
Assert::equals(
166+
['id' => 'one', 'topic' => 'Changed'],
167+
$fixture->update(['topic' => 'Changed'])->properties()
168+
);
169+
}
170+
171+
#[Test]
172+
public function change_nested_field_via_update() {
173+
$fixture= new Document(['id' => 'one', 'topic' => ['en' => 'Test', 'de' => 'Test']]);
174+
175+
Assert::equals(
176+
['id' => 'one', 'topic' => ['en' => 'Changed', 'de' => 'Test']],
177+
$fixture->update(['topic.en' => 'Changed'])->properties()
178+
);
179+
}
180+
181+
#[Test]
182+
public function add_field_via_with() {
183+
$fixture= new Document(['id' => 'one']);
184+
185+
Assert::equals(
186+
['id' => 'one', 'topic' => 'Test'],
187+
$fixture->with('topic', 'Test')->properties()
188+
);
189+
}
190+
191+
#[Test]
192+
public function change_field_via_with() {
193+
$fixture= new Document(['id' => 'one', 'topic' => 'Test']);
194+
195+
Assert::equals(
196+
['id' => 'one', 'topic' => 'Changed'],
197+
$fixture->with('topic', 'Changed')->properties()
198+
);
199+
}
200+
201+
#[Test]
202+
public function change_nested_field_via_with() {
203+
$fixture= new Document(['id' => 'one', 'topic' => ['en' => 'Test', 'de' => 'Test']]);
204+
205+
Assert::equals(
206+
['id' => 'one', 'topic' => ['en' => 'Changed', 'de' => 'Test']],
207+
$fixture->with('topic.en', 'Changed')->properties()
208+
);
209+
}
210+
211+
#[Test]
212+
public function remove_field() {
213+
$fixture= new Document(['id' => 'one', 'topic' => 'Test']);
214+
215+
Assert::equals(
216+
['topic' => 'Test'],
217+
$fixture->unset('id')->properties()
218+
);
219+
}
220+
221+
#[Test]
222+
public function remove_fields() {
223+
$fixture= new Document(['id' => 'one', 'connections' => [], 'topic' => 'Test']);
224+
225+
Assert::equals(
226+
['topic' => 'Test'],
227+
$fixture->unset('id', 'connections')->properties()
228+
);
229+
}
230+
231+
#[Test]
232+
public function remove_non_existant_field() {
233+
$fixture= new Document(['topic' => 'Test']);
234+
235+
Assert::equals(
236+
['topic' => 'Test'],
237+
$fixture->unset('id')->properties()
238+
);
239+
}
240+
241+
#[Test]
242+
public function remove_nested_field() {
243+
$fixture= new Document(['id' => 'one', 'topic' => ['en' => 'Test', 'de' => 'Test']]);
244+
245+
Assert::equals(
246+
['id' => 'one', 'topic' => ['de' => 'Test']],
247+
$fixture->unset('topic.en')->properties()
248+
);
249+
}
250+
118251
#[Test]
119252
public function create() {
120253
Assert::instance(ObjectId::class, Document::create()->id());

0 commit comments

Comments
 (0)