globyapp/odata-query-parser

Parse OData v4 query strings, outputs proper PHP objects.

v1.0.0 2024-05-06 20:20 UTC

This package is auto-updated.

Last update: 2024-05-06 20:24:19 UTC


README

Parse OData v4 query strings, outputs proper PHP objects.

Packagist Version Packagist PHP from Packagist CI

Summary

About

I needed to only parse query strings to convert OData v4 commands into an understandable array that I could use to make a Laravel package to offer a way to automatically use Eloquent to filter the response according to this parsed array of OData v4 command.

As I did not see a package exclusively dealing with parsing the query strings, and saw that some people worked on their own without open sourcing it, I decided I would start one myself.

Features

  • Parses an URL and returns an array
  • Supports $select, $top, $skip, $orderby, $count
  • Partial support for $filter (see Known issues section)
  • You can use a parse mode that let you parse these keywords without prepending $

Requirements

Installation

Add the package to your dependencies:

composer require globyapp/odata-query-parser

Examples

1. Use $select to filter on some fields

In this example, we will use the $select OData query string command to filter the fields returned by our API.

use GlobyApp\OdataQueryParser\OdataQueryParser;

$data = OdataQueryParser::parse('https://example.com/api/user?$select=id,name,age');

If you inspect $data, this is what you will get:

object(GlobyApp\OdataQueryParser\OdataQuery)#2 (6) {
  ["select":"GlobyApp\OdataQueryParser\OdataQuery":private]=>
  array(3) {
    [0]=>
    string(2) "id"
    [1]=>
    string(4) "name"
    [2]=>
    string(3) "age"
  }
  ...
}

2. Use non dollar syntax

In this example, we will use a unique feature of this library: to be able to not specify any dollar, while still being able to use the OData v4 URL query parameter grammar.

use GlobyApp\OdataQueryParser\OdataQueryParser;

$data = OdataQueryParser::parse("https://example.com/api/user?select=id,name,age", $withDollar = false);

If you inspect $data, this is what you will get:

object(GlobyApp\OdataQueryParser\OdataQuery)#2 (6) {
  ["select":"GlobyApp\OdataQueryParser\OdataQuery":private]=>
  array(3) {
    [0]=>
    string(2) "id"
    [1]=>
    string(4) "name"
    [2]=>
    string(3) "age"
  }
  ...
}

API

OdataQueryParser::parse(string $url, bool $withDollar = true): OdataQuery;

Parameters

  • string $url: The URL to parse the query strings from. It should be a "complete" or "full" URL, which means that https://example.com will pass while example.com will not pass
  • bool $withDollar: Set it to false if you want to parse query strings without having to add the $ signs before each key.

Returns

An OdataQuery object:

return = OdataQuery {
	select => array<string>,
	count => bool|null,
	top => int|null,
	skip => int|null,
	orderBy => array<OrderByClause>,
	filter => array<FilterClause>
};

OrderByClause {
	property => string,
	direction => OrderDirection
}

OrderDirection = "ASC" | "DESC"

FilterClause {
	property => string,
	operator => string,
	value => int|float|string|bool|null|array<int|float|string|bool|null>
}

Throws

  • InvalidArgumentException
    • If the parameter $url is not a valid URL (see the parameter description to know what is a valid URL)
    • If the $top query string value is not an integer
    • If the $top query string value is lower than 0
    • If the $skip query string value is not an integer
    • If the $skip query string value is lower than 0
    • If the $count query string value is not a boolean
    • If the formatting of $orderby is not valid (should be a property, space and the direction)
    • If the direction of the $orderby query string value is neither asc or desc (case-insensitive)
    • If the formatting of $filter is not valid (should be a property, space, operator, space and value)
    • If the operator of the $filter query string value is not eq, ne, gt, ge, lt, le or in (case-insensitive)
  • LogicException
    • If an unforeseen edge case is triggered by an input value. For example when a regex operation fails. Should never be thrown under normal operation.
      • If an edge case is found, please report them as an issue. Currently, I cannot write test cases for them as I don't know how to trigger them.

Known issues

  • $filter command will not parse or and functions (like contains() of substringof), because I did not focus on this for the moment (the parser for $filter is too simplistic, I should find a way to create an AST).

Thanks

Feel free to open any issues or PRs.

MIT © 2024