Skip to content

Commit 3b14131

Browse files
authored
Add whereContains, orWhereContains, whereNotContains, orWhereNotContains (#169)
Closes #148 and #99
1 parent 5353b3f commit 3b14131

File tree

5 files changed

+275
-1
lines changed

5 files changed

+275
-1
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "saintsystems/odata-client",
3-
"version": "0.10.1",
3+
"version": "0.10.2",
44
"description": "Saint Systems OData Client for PHP",
55
"keywords": [
66
"odata",

src/Query/Builder.php

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,61 @@ public function orWhereNotIn($column, $list)
844844
return $this->whereNotIn($column, $list, 'or');
845845
}
846846

847+
/**
848+
* Add a "where contains" clause to the query.
849+
*
850+
* @param string $column
851+
* @param string $value
852+
* @param string $boolean
853+
* @param bool $not
854+
* @return $this
855+
*/
856+
public function whereContains($column, $value, $boolean = 'and', $not = false)
857+
{
858+
$type = $not ? 'NotContains' : 'Contains';
859+
860+
$this->wheres[] = compact('type', 'column', 'value', 'boolean');
861+
862+
return $this;
863+
}
864+
865+
/**
866+
* Add an "or where contains" clause to the query.
867+
*
868+
* @param string $column
869+
* @param string $value
870+
* @return Builder|static
871+
*/
872+
public function orWhereContains($column, $value)
873+
{
874+
return $this->whereContains($column, $value, 'or');
875+
}
876+
877+
/**
878+
* Add a "where not contains" clause to the query.
879+
*
880+
* @param string $column
881+
* @param string $value
882+
* @param string $boolean
883+
* @return Builder|static
884+
*/
885+
public function whereNotContains($column, $value, $boolean = 'and')
886+
{
887+
return $this->whereContains($column, $value, $boolean, true);
888+
}
889+
890+
/**
891+
* Add an "or where not contains" clause to the query.
892+
*
893+
* @param string $column
894+
* @param string $value
895+
* @return Builder|static
896+
*/
897+
public function orWhereNotContains($column, $value)
898+
{
899+
return $this->whereNotContains($column, $value, 'or');
900+
}
901+
847902

848903

849904
/**

src/Query/Grammar.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,32 @@ protected function whereNotIn(Builder $query, $where)
683683
return 'not(' . $where['column'] . ' in (\'' . implode('\',\'', $where['list']) . '\'))';
684684
}
685685

686+
/**
687+
* Compile a "where contains" clause.
688+
*
689+
* @param Builder $query
690+
* @param array $where
691+
* @return string
692+
*/
693+
protected function whereContains(Builder $query, $where)
694+
{
695+
$value = $this->prepareValue($where['value']);
696+
return 'contains(' . $where['column'] . ',' . $value . ')';
697+
}
698+
699+
/**
700+
* Compile a "where not contains" clause.
701+
*
702+
* @param Builder $query
703+
* @param array $where
704+
* @return string
705+
*/
706+
protected function whereNotContains(Builder $query, $where)
707+
{
708+
$value = $this->prepareValue($where['value']);
709+
return 'indexof(' . $where['column'] . ',' . $value . ') eq -1';
710+
}
711+
686712
/**
687713
* Append query param to existing uri
688714
*

tests/ODataClientTest.php

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,4 +314,119 @@ public function testODataClientCursorPageSizeOf20ShouldReturnAllEntities()
314314

315315
$this->assertEquals($pageSize, count($data->toArray()));
316316
}
317+
318+
public function testODataClientWhereContainsRealData()
319+
{
320+
$odataClient = $this->createODataClient();
321+
322+
// Test whereContains - should find airlines with "Airline" in their name
323+
$airlines = $odataClient->from('Airlines')
324+
->whereContains('Name', 'Airline')
325+
->get();
326+
327+
$this->assertTrue(is_array($airlines->toArray()));
328+
$this->assertGreaterThan(0, $airlines->count());
329+
330+
// Verify all results contain "Airline" in the name
331+
foreach ($airlines as $airline) {
332+
$this->assertStringContainsString('Airline', $airline->Name);
333+
}
334+
335+
// Should find American Airlines, Shanghai Airline, and Austrian Airlines
336+
$airlineCodes = $airlines->pluck('AirlineCode')->toArray();
337+
$this->assertContains('AA', $airlineCodes); // American Airlines
338+
$this->assertContains('FM', $airlineCodes); // Shanghai Airline
339+
$this->assertContains('OS', $airlineCodes); // Austrian Airlines
340+
}
341+
342+
public function testODataClientOrWhereContainsRealData()
343+
{
344+
$odataClient = $this->createODataClient();
345+
346+
// Test orWhereContains - find Air France OR names containing "China"
347+
$airlines = $odataClient->from('Airlines')
348+
->where('AirlineCode', 'AF')
349+
->orWhereContains('Name', 'China')
350+
->get();
351+
352+
$this->assertTrue(is_array($airlines->toArray()));
353+
$this->assertGreaterThanOrEqual(2, $airlines->count()); // At least Air France and China Eastern
354+
355+
$airlineCodes = $airlines->pluck('AirlineCode')->toArray();
356+
$this->assertContains('AF', $airlineCodes); // Air France
357+
$this->assertContains('MU', $airlineCodes); // China Eastern Airlines
358+
}
359+
360+
public function testODataClientWhereNotContainsRealData()
361+
{
362+
$odataClient = $this->createODataClient();
363+
364+
// Test whereNotContains - should find airlines WITHOUT "Airline" in their name
365+
$airlines = $odataClient->from('Airlines')
366+
->whereNotContains('Name', 'Airline')
367+
->get();
368+
369+
$this->assertTrue(is_array($airlines->toArray()));
370+
$this->assertGreaterThan(0, $airlines->count());
371+
372+
// Verify none of the results contain "Airline" in the name
373+
foreach ($airlines as $airline) {
374+
$this->assertStringNotContainsString('Airline', $airline->Name);
375+
}
376+
377+
// Should find Air France, Alitalia, Air Canada, and other non-Airline named carriers
378+
$airlineCodes = $airlines->pluck('AirlineCode')->toArray();
379+
$this->assertContains('AF', $airlineCodes); // Air France
380+
$this->assertContains('AZ', $airlineCodes); // Alitalia
381+
$this->assertContains('AC', $airlineCodes); // Air Canada
382+
$this->assertNotContains('AA', $airlineCodes); // American Airlines should NOT be included
383+
$this->assertNotContains('FM', $airlineCodes); // Shanghai Airline should NOT be included
384+
}
385+
386+
public function testODataClientOrWhereNotContainsRealData()
387+
{
388+
$odataClient = $this->createODataClient();
389+
390+
// Test orWhereNotContains - find Turkish Airlines OR names not containing "Air"
391+
$airlines = $odataClient->from('Airlines')
392+
->where('AirlineCode', 'TK')
393+
->orWhereNotContains('Name', 'Air')
394+
->get();
395+
396+
$this->assertTrue(is_array($airlines->toArray()));
397+
$this->assertGreaterThan(0, $airlines->count());
398+
399+
$airlineCodes = $airlines->pluck('AirlineCode')->toArray();
400+
$this->assertContains('TK', $airlineCodes); // Turkish Airlines (matched by first condition)
401+
$this->assertContains('AZ', $airlineCodes); // Alitalia (no "Air" in name)
402+
// Note: Results may vary based on the current dataset
403+
}
404+
405+
public function testODataClientCombinedContainsNotContainsRealData()
406+
{
407+
$odataClient = $this->createODataClient();
408+
409+
// Test combined contains/notContains - names with "Air" but not "Airline"
410+
$airlines = $odataClient->from('Airlines')
411+
->whereContains('Name', 'Air')
412+
->whereNotContains('Name', 'Airline')
413+
->get();
414+
415+
$this->assertTrue(is_array($airlines->toArray()));
416+
$this->assertGreaterThan(0, $airlines->count());
417+
418+
// Should find Air France, Air Canada, Alitalia
419+
$airlineNames = $airlines->pluck('Name')->toArray();
420+
foreach ($airlineNames as $name) {
421+
$this->assertStringContainsString('Air', $name);
422+
$this->assertStringNotContainsString('Airline', $name);
423+
}
424+
425+
$airlineCodes = $airlines->pluck('AirlineCode')->toArray();
426+
$this->assertContains('AF', $airlineCodes); // Air France
427+
$this->assertContains('AC', $airlineCodes); // Air Canada
428+
$this->assertNotContains('AA', $airlineCodes); // American Airlines (contains both)
429+
$this->assertNotContains('OS', $airlineCodes); // Austrian Airlines (contains both)
430+
$this->assertNotContains('FM', $airlineCodes); // Shanghai Airline (contains both)
431+
}
317432
}

tests/Query/BuilderTest.php

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,4 +695,82 @@ public function testEntityMultipleWheresNestedWithSelect()
695695
$this->assertEquals($expectedUri, $actualUri);
696696
}
697697

698+
public function testEntityWithWhereContains()
699+
{
700+
$builder = $this->getBuilder();
701+
702+
$entitySet = 'Airlines';
703+
704+
$builder->from($entitySet)
705+
->whereContains('Name', 'Airline');
706+
707+
$expectedUri = 'Airlines?$filter=contains(Name,\'Airline\')';
708+
$actualUri = $builder->toRequest();
709+
710+
$this->assertEquals($expectedUri, $actualUri);
711+
}
712+
713+
public function testEntityWithOrWhereContains()
714+
{
715+
$builder = $this->getBuilder();
716+
717+
$entitySet = 'Airlines';
718+
719+
$builder->from($entitySet)
720+
->where('AirlineCode', 'AA')
721+
->orWhereContains('Name', 'Airline');
722+
723+
$expectedUri = 'Airlines?$filter=AirlineCode eq \'AA\' or contains(Name,\'Airline\')';
724+
$actualUri = $builder->toRequest();
725+
726+
$this->assertEquals($expectedUri, $actualUri);
727+
}
728+
729+
public function testEntityWithWhereNotContains()
730+
{
731+
$builder = $this->getBuilder();
732+
733+
$entitySet = 'Airlines';
734+
735+
$builder->from($entitySet)
736+
->whereNotContains('Name', 'Airline');
737+
738+
$expectedUri = 'Airlines?$filter=indexof(Name,\'Airline\') eq -1';
739+
$actualUri = $builder->toRequest();
740+
741+
$this->assertEquals($expectedUri, $actualUri);
742+
}
743+
744+
public function testEntityWithOrWhereNotContains()
745+
{
746+
$builder = $this->getBuilder();
747+
748+
$entitySet = 'Airlines';
749+
750+
$builder->from($entitySet)
751+
->where('AirlineCode', 'AA')
752+
->orWhereNotContains('Name', 'Airline');
753+
754+
$expectedUri = 'Airlines?$filter=AirlineCode eq \'AA\' or indexof(Name,\'Airline\') eq -1';
755+
$actualUri = $builder->toRequest();
756+
757+
$this->assertEquals($expectedUri, $actualUri);
758+
}
759+
760+
public function testEntityWithMultipleContains()
761+
{
762+
$builder = $this->getBuilder();
763+
764+
$entitySet = 'Airlines';
765+
766+
$builder->from($entitySet)
767+
->whereContains('Name', 'Air')
768+
->whereNotContains('Name', 'Airline');
769+
770+
$expectedUri = 'Airlines?$filter=contains(Name,\'Air\') and indexof(Name,\'Airline\') eq -1';
771+
$actualUri = $builder->toRequest();
772+
773+
$this->assertEquals($expectedUri, $actualUri);
774+
}
775+
698776
}

0 commit comments

Comments
 (0)