PHP code example of novabytes / odata-query-parser

1. Go to this page and download the library: Download novabytes/odata-query-parser library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

novabytes / odata-query-parser example snippets


use NovaBytes\OData\Parser\QueryOptionParser;

$query = QueryOptionParser::parse(
    '$filter=Price gt 100 and contains(Name,\'Widget\')'
    . '&$select=Name,Price'
    . '&$expand=Category($select=Name;$top=5)'
    . '&$orderby=Price desc'
    . '&$top=50&$skip=10&$count=true'
);

$query->filter;   // BinaryExpression (and)
$query->select;   // [SelectItem('Name'), SelectItem('Price')]
$query->expand;   // [ExpandItem('Category', nestedOptions: ...)]
$query->orderby;  // [OrderByItem(PropertyPath('Price'), Desc)]
$query->top;      // 50
$query->skip;     // 10
$query->count;    // true

use NovaBytes\OData\Parser\FilterParser;
use NovaBytes\OData\Parser\SelectParser;
use NovaBytes\OData\Parser\OrderByParser;

$filter = FilterParser::parse('Price gt 100 and contains(Name,\'Widget\')');
$select = SelectParser::parse('Name,Price,Address/City');
$orderby = OrderByParser::parse('Name asc,Price desc');

// Comparison operators: eq, ne, gt, ge, lt, le
FilterParser::parse('Price gt 100');
FilterParser::parse('Name eq \'Milk\'');

// Logical operators: and, or, not
FilterParser::parse('Price gt 5 and Price lt 20');
FilterParser::parse('not contains(Name,\'test\')');

// Arithmetic operators: add, sub, mul, div, divby, mod
FilterParser::parse('Price mul Quantity gt 1000');

// Grouping with parentheses
FilterParser::parse('(Name eq \'A\' or Name eq \'B\') and Price lt 10');

// Property paths
FilterParser::parse('Address/City eq \'London\'');

// The in operator
FilterParser::parse('Name in (\'Milk\',\'Cheese\',\'Butter\')');

// String functions
FilterParser::parse('contains(Name,\'milk\')');
FilterParser::parse('startswith(Name,\'Ch\')');
FilterParser::parse('endswith(Name,\'ilk\')');
FilterParser::parse('length(Name) gt 5');
FilterParser::parse('indexof(Name,\'lk\') eq 2');
FilterParser::parse('substring(Name,1,3) eq \'ilk\'');
FilterParser::parse('tolower(Name) eq \'milk\'');
FilterParser::parse('toupper(Name) eq \'MILK\'');
FilterParser::parse('trim(Name) eq \'Milk\'');
FilterParser::parse('concat(FirstName,LastName) eq \'JohnDoe\'');

// Date/time functions
FilterParser::parse('year(BirthDate) eq 1990');
FilterParser::parse('month(BirthDate) eq 3');
FilterParser::parse('day(BirthDate) eq 20');
FilterParser::parse('hour(StartTime) ge 9');
FilterParser::parse('Date gt now()');

// Math functions
FilterParser::parse('round(Price) eq 10');
FilterParser::parse('floor(Price) eq 9');
FilterParser::parse('ceiling(Price) eq 10');

// any — true if any element matches
FilterParser::parse('Items/any(d:d/Qty gt 100)');

// any without predicate — true if collection is non-empty
FilterParser::parse('Tags/any()');

// all — true if all elements match
FilterParser::parse('Items/all(d:d/Price gt 0)');

FilterParser::parse('Active eq true');                                    // boolean
FilterParser::parse('Name eq null');                                      // null
FilterParser::parse('Count eq 42');                                       // integer
FilterParser::parse('Price lt 9.99');                                     // decimal
FilterParser::parse('Name eq \'O\'\'Brien\'');                            // string (escaped quotes)
FilterParser::parse('Id eq 01234567-89ab-cdef-0123-456789abcdef');        // GUID
FilterParser::parse('BirthDate eq 2023-01-15');                           // date
FilterParser::parse('Created eq 2023-01-15T14:30:00Z');                   // DateTimeOffset
FilterParser::parse('Duration eq duration\'P1DT2H30M\'');                 // duration

$items = SelectParser::parse('Name,Price,Address/City');
// [SelectItem(['Name']), SelectItem(['Price']), SelectItem(['Address', 'City'])]

$items = SelectParser::parse('*');
// [SelectItem([], isWildcard: true)]

use NovaBytes\OData\Parser\ExpandParser;

$items = ExpandParser::parse('Products,Category');
// [ExpandItem(['Products']), ExpandItem(['Category'])]

// With nested query options (semicolon-separated inside parentheses)
$items = ExpandParser::parse('Products($filter=Price gt 100;$select=Name;$top=5)');
// ExpandItem(['Products'], nestedOptions: QueryOptions(filter: ..., select: ..., top: 5))

$items = OrderByParser::parse('Name asc,Price desc');
// [OrderByItem(PropertyPath('Name'), Asc), OrderByItem(PropertyPath('Price'), Desc)]

// Default direction is ascending
$items = OrderByParser::parse('Name');
// [OrderByItem(PropertyPath('Name'), Asc)]

$query = QueryOptionParser::parse('$top=10&$skip=20&$count=true');
$query->top;   // 10
$query->skip;  // 20
$query->count; // true

use NovaBytes\OData\Parser\ResourcePathParser;

// Entity set collection
$path = ResourcePathParser::parse('/Products');
$path->entitySet;          // 'Products'
$path->key;                // null
$path->isSingleEntity();   // false

// Single entity by key
$path = ResourcePathParser::parse('/Products(1)');
$path->key->getSingleValue();  // 1

// String keys
$path = ResourcePathParser::parse("/Products('abc')");
$path->key->getSingleValue();  // 'abc'

// Composite keys
$path = ResourcePathParser::parse('/OrderItems(OrderId=1,ItemId=2)');
$path->key->values;  // ['OrderId' => 1, 'ItemId' => 2]

// Navigation segments
$path = ResourcePathParser::parse('/Products(1)/Category');
$path->navigationSegments[0]->property;  // 'Category'

// GUID keys
$path = ResourcePathParser::parse('/Products(01234567-89ab-cdef-0123-456789abcdef)');

use NovaBytes\OData\AST\Filter\BinaryExpression;
use NovaBytes\OData\AST\Filter\Literal;
use NovaBytes\OData\AST\Filter\PropertyPath;
use NovaBytes\OData\Visitor\ExpressionVisitor;

class SqlWhereVisitor implements ExpressionVisitor
{
    public function visitBinaryExpression(BinaryExpression $expr): string
    {
        $left = $this->visit($expr->left);
        $right = $this->visit($expr->right);
        $op = match($expr->operator) {
            BinaryOperator::Eq => '=',
            BinaryOperator::Ne => '!=',
            BinaryOperator::Gt => '>',
            // ...
        };
        return "{$left} {$op} {$right}";
    }

    public function visitPropertyPath(PropertyPath $expr): string
    {
        return implode('.', $expr->segments);
    }

    public function visitLiteral(Literal $expr): string
    {
        // Use parameterized queries in real code!
        return match($expr->type) {
            LiteralType::String => "'{$expr->value}'",
            LiteralType::Null => 'NULL',
            default => (string) $expr->value,
        };
    }

    // ... implement remaining visit methods
}

use NovaBytes\OData\Visitor\StringifyVisitor;

$expr = FilterParser::parse('Price gt 100 and Name eq \'Milk\'');
$visitor = new StringifyVisitor();
echo $visitor->stringify($expr);
// Price gt 100 and Name eq 'Milk'

try {
    FilterParser::parse('Price gtt 100');
} catch (ParseException $e) {
    $e->getMessage();  // "Unexpected 'gtt' at position 6; expected ..."
    $e->position;      // 6
}

use NovaBytes\OData\Metadata\EntityType;
use NovaBytes\OData\Metadata\PropertyMetadata;
use NovaBytes\OData\Metadata\NavigationPropertyMetadata;

$product = new EntityType(
    name: 'Product',
    entitySetName: 'Products',
    keyProperty: 'Id',
    properties: [
        new PropertyMetadata('Id', 'Edm.Int64', nullable: false, filterable: true, sortable: true, selectable: true),
        new PropertyMetadata('Name', 'Edm.String', nullable: false, filterable: true, sortable: true, selectable: true, creatable: true, updatable: true),
        new PropertyMetadata('Price', 'Edm.Decimal', nullable: false, filterable: true, sortable: true, selectable: true, creatable: true, updatable: true),
    ],
    navigationProperties: [
        new NavigationPropertyMetadata('Category', 'Category', isCollection: false),
        new NavigationPropertyMetadata('Reviews', 'Review', isCollection: true),
    ],
    operations: ['read', 'create', 'update', 'delete'],
);

use NovaBytes\OData\Metadata\EdmTypeResolver;

EdmTypeResolver::resolve('integer');   // 'Edm.Int32'
EdmTypeResolver::resolve('varchar');   // 'Edm.String'
EdmTypeResolver::resolve('timestamp'); // 'Edm.DateTimeOffset'
EdmTypeResolver::resolve('boolean');   // 'Edm.Boolean'

use NovaBytes\OData\Metadata\CsdlGenerator;

$xml = CsdlGenerator::generate('MyApp', [$product, $category]);
// Returns valid OData v4 CSDL XML with capability annotations

use NovaBytes\OData\Metadata\OpenApiGenerator;

$spec = OpenApiGenerator::generate([$product, $category], [
    'title' => 'My OData API',
    'version' => '1.0.0',
]);
// Generates:
//   GET /Products           — list entities
//   POST /Products          — create entity (with ProductCreate schema)
//   GET /Products({Id})     — get single entity
//   PUT /Products({Id})     — full replace (with ProductUpdate schema)
//   PATCH /Products({Id})   — partial update
//   DELETE /Products({Id})  — delete entity
echo json_encode($spec, JSON_PRETTY_PRINT);