{"id":48380900,"url":"https://github.com/e2see/waveql","last_synced_at":"2026-04-05T20:02:57.528Z","repository":{"id":342630970,"uuid":"1174602152","full_name":"e2see/waveQl","owner":"e2see","description":"waveQl – SQL builder where \u003e18, ~text~, 10\u003e\u003c20 write themselves. No where() chains, just intuitive filters.","archived":false,"fork":false,"pushed_at":"2026-04-03T18:08:41.000Z","size":87,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-03T20:28:25.442Z","etag":null,"topics":["crud","database","filter","mysql","mysqli","pagination","php","prepared-statements","query-builder","sql"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/e2see.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-06T16:21:28.000Z","updated_at":"2026-04-03T18:05:19.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/e2see/waveQl","commit_stats":null,"previous_names":["e2see/waveql"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/e2see/waveQl","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/e2see%2FwaveQl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/e2see%2FwaveQl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/e2see%2FwaveQl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/e2see%2FwaveQl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/e2see","download_url":"https://codeload.github.com/e2see/waveQl/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/e2see%2FwaveQl/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31448219,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-05T15:22:31.103Z","status":"ssl_error","status_checked_at":"2026-04-05T15:22:00.205Z","response_time":75,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["crud","database","filter","mysql","mysqli","pagination","php","prepared-statements","query-builder","sql"],"created_at":"2026-04-05T20:02:45.385Z","updated_at":"2026-04-05T20:02:57.523Z","avatar_url":"https://github.com/e2see.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Demo](images/logo-s.png)\n\n\n[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) a smart query builder\n\n\n# waveQl – The SQL Builder That Speaks Your Language\n\n\n\n\n**waveQl** is not just another boring query builder. It thinks with you.\nYou tell it which fields you have, and it builds a perfect SQL query – including **joins, pagination, sorting** and above all: **operators directly inside the value**.\n\n\u003e 👉 `\"\u003e10\"`, `\"!NULL\"`, `\"~text~\"`, `\"10\u003e\u003c20\"` – that's not rocket science, that's waveQl.\n\n\n\n\u003cbr\u003e\u003cbr\u003e\n◤◤◤ Why waveQl?\n\nBecause filtering should be fun.\nLook at this:\n\n```php\n$input = [\n    'FoundedYear'   =\u003e '1900\u003e\u003c=1950',   // between 1901 and 1950 (inclusive)\n    'ContinentName' =\u003e 'Asia',\n    '~or~'          =\u003e [\n        'Population' =\u003e '\u003e60000000',\n        'AreaKm2'    =\u003e '\u003e2200000'\n    ]\n];\n```\n\nNo where() chains, no callbacks – just clean, readable values.\nwaveQl automatically parses the operators and builds the correct SQL condition.\n\nAnd that's just the beginning.\n\nFeatures – what awaits you\n\n- Operator parsing – `\u003c`, `\u003e`, `\u003c=`, `\u003e=`, `!`, `~like~`, `!NULL`, `BLANK`, `EMPTY` and even ranges like `10\u003e\u003c20` or `5\u003e\u003c=15`.\n- Magic keys – `BLANK`, `!BLANK`, `EMPTY`, `!EMPTY` – automatically adapt to the field type (string, number, date).\n- Automatic date fields – from a date field you get `fieldYEAR`, `fieldMONTH`, `fieldDAY`, `fieldTIME`, `fieldUTS` – without extra code.\n- Joins – `LEFT`, `RIGHT`, `INNER`, `CROSS`, `STRAIGHT` – all there.\n- Pagination \u0026 sorting – via `pageNumber`, `pageSize` and sort (e.g. '\u003ename,\u003cid').\n- Fulltext search – with `searchString` and `searchTarget`.\n- Custom SQL – security‑checked, with placeholder replacement.\n- Prepared statements – optional but highly recommended.\n- Flat OR groups – simply `'~or~' =\u003e ['field' =\u003e 'value', ...]`.\n\n\n\n\u003cbr\u003e\u003cbr\u003e\n◤◤◤ Quick Example\n\nImagine you have the following countries and continents tables.\nThis is how easy it is with waveQl:\n\n```php\n\n// waveQl – Because filtering should be intuitive\n\nrequire_once 'waveQl.php';\n\n\n// Table and join configuration\n$tableManifest = [\n    'tableName' =\u003e 'countries',\n    'tableKey'  =\u003e 'c',\n    'joinList'  =\u003e [\n        [\n            'type'          =\u003e 'LEFT',\n            'tableName'     =\u003e 'continents',\n            'tableKey'      =\u003e 'cnt',\n            'connectColumn' =\u003e 'id',\n            'connectWith'   =\u003e 'c.continent_id'\n        ]\n    ]\n];\n\n// Field definitions (logical name =\u003e SQL column + type)\n$keyManifest = [\n    'CountryName'    =\u003e ['rowName' =\u003e 'c.name',          'type' =\u003e 'string'],\n    'Population'     =\u003e ['rowName' =\u003e 'c.population',    'type' =\u003e 'integer'],\n    'AreaKm2'        =\u003e ['rowName' =\u003e 'c.area_km2',      'type' =\u003e 'integer'],\n    'Capital'        =\u003e ['rowName' =\u003e 'c.capital',       'type' =\u003e 'string'],\n    'FoundedYear'    =\u003e ['rowName' =\u003e 'c.founded_year',  'type' =\u003e 'integer'],\n    'FoundedDate'    =\u003e ['rowName' =\u003e 'c.founded_date',  'type' =\u003e 'dateTime'],\n    'ContinentName'  =\u003e ['rowName' =\u003e 'cnt.name',        'type' =\u003e 'string'],\n    '~meta~'         =\u003e [   // default meta settings (sort, pageSize, searchTarget)\n        'sort'         =\u003e '\u003eCountryName',\n        'pageSize'     =\u003e 20,\n        'searchTarget' =\u003e 'CountryName,Capital,ContinentName'\n    ],\n];\n\n$db      = new mysqli('localhost', 'root', '', 'mydb');\n$wave    = \\e2\\waveQl::create($db, $tableManifest, $keyManifest);\n$builder = $wave-\u003eread(); // read-modus (also possible: write for INSERTS)\n\n```\n\nThat was the one-time setup – now the real filter fun begins!\n\n```php\n\n$filters = [\n    'FoundedYear'   =\u003e '1900\u003e\u003c=1950',   // between 1901 and 1950 (inclusive)\n    'ContinentName' =\u003e 'Asia',\n    '~or~'          =\u003e [\n        'Population' =\u003e '\u003e60000000',\n        'AreaKm2'    =\u003e '\u003e2200000'\n    ]\n];\n\n\n// get the final SQL\necho $builder-\u003esetValues($filters)-\u003egetQuery();\n\n\n```\n\n\n\nThe resulting SQL – clean and powerful:\n\n```sql\n\n\nSELECT\n    `c`.`name`               AS CountryName,\n    `c`.`population`         AS Population,\n    `c`.`area_km2`           AS AreaKm2,\n    `c`.`capital`            AS Capital,\n    `c`.`founded_year`       AS FoundedYear,\n    `c`.`founded_date`       AS FoundedDate,\n    DATE(c.founded_date)     AS FoundedDateDATE,\n    YEAR(c.founded_date)     AS FoundedDateYEAR,\n    QUARTER(c.founded_date)  AS FoundedDateQUARTER,\n    MONTH(c.founded_date)    AS FoundedDateMONTH,\n    DAY(c.founded_date)      AS FoundedDateDAY,\n    TIME(c.founded_date)     AS FoundedDateTIME,\n    HOUR(c.founded_date)     AS FoundedDateHOUR,\n    MINUTE(c.founded_date)   AS FoundedDateMINUTE,\n    UNIX_TIMESTAMP(c.founded_date) AS FoundedDateUTS,\n    `cnt`.`name`             AS ContinentName\nFROM\n    `countries` `c`\n        LEFT JOIN\n            `continents` `cnt`\n            ON (`cnt`.`id` = `c`.`continent_id`)\nWHERE 1\n    AND `c`.`founded_year`   \u003c= 1950\n    AND `c`.`founded_year`   \u003e 1900\n    AND `cnt`.`name`         = 'Asia'\n    AND (\n            (`c`.`population` \u003e 60000000)\n         OR (`c`.`area_km2`  \u003e 2200000)\n    )\nORDER BY\n    `CountryName`            DESC\nLIMIT\n    0, 20\n\n\n\n```\n\n\nDone. No manual WHERE fiddling, no mistakes with forgotten parentheses.\n\n\u003cbr /\u003e\n\nAnd there you have it – the matching records, complete with those handy auto‑generated date/time columns.\n\n\n```sql\n\nCountryName       | Turkey          | Pakistan        | Indonesia       | India\nPopulation        | 84,300,000      | 220,000,000     | 273,000,000     | 1,380,000,000\nAreaKm2           | 783,600         | 881,900         | 1,905,000       | 3,287,000\nCapital           | Ankara          | Islamabad       | Jakarta         | New Delhi\nFoundedYear       | 1923            | 1947            | 1945            | 1947\nFoundedDateDATE   | 1923-10-29      | 1947-08-14      | 1945-08-17      | 1947-08-15\nFoundedDateYEAR   | 1923            | 1947            | 1945            | 1947\nFoundedDateQUARTER| 4               | 3               | 3               | 3\nFoundedDateMONTH  | 10              | 8               | 8               | 8\nFoundedDateDAY    | 29              | 14              | 17              | 15\nFoundedDateTIME   | 00:00:00        | 00:00:00        | 00:00:00        | 00:00:00\nFoundedDateHOUR   | 0               | 0               | 0               | 0\nFoundedDateMINUTE | 0               | 0               | 0               | 0\nFoundedDateUTS    | NULL            | NULL            | NULL            | NULL\nFoundedDate       | 1923-10-29      | 1947-08-14      | 1945-08-17      | 1947-08-15\nContinentName     | Asia            | Asia            | Asia            | Asia\n\n```\n\nWant to give it a spin yourself? The included demo UI lets you experiment with all operators, magic keys, and joins live.\n\n\n\u003cbr\u003e\u003cbr\u003e\n◤◤◤ Status of Write Operations\n\n**Note:** Write operations (`INSERT`, `UPDATE`, `DELETE`) are already present in the codebase but are **not yet considered stable**. The API may still change. For production use, only read operations (`SELECT`) are recommended at this time. Write support will be finalized in one of the next releases.\n\n\n\u003cbr\u003e\u003cbr\u003e\n◤◤◤ Installation\n\n**No Composer required** – just copy the waveQl files into your project and include them manually.\nIf you prefer Composer, you can add the repository to your `composer.json` (the package is not yet published on Packagist).\n\n**Requirements**\n- PHP ≥8.0\n- A database connection: either a `mysqli` object **or** an object implementing the `waveQlDbInterface` (e.g. a custom PDO wrapper).\n\nNo other dependencies – just PHP ≥8.1 and a database connection (mysqli or a custom adapter).\n\n\n\n\n\u003cbr\u003e\u003cbr\u003e\n◤◤◤ Demo Environment\n\nThe repository includes a **ready‑to‑run demo environment**. Just copy the waveQl files into the parent directory and point your browser to `index.php`. You can explore all operators, magic keys, and joins interactively.\n\nA **database setup script** is included – simply click the *\"reset/initialise database\"* link to create the demo tables and sample data. The interface lets you apply filters, see the generated SQL, and execute queries in real time. It's the perfect sandbox to experiment with waveQl's features.\n\n\n\u003cbr\u003e\u003cbr\u003e\n◤◤◤ More\n\nThe complete documentation with all operators, magic keys, and configuration options is in the class DocBlock – take a look at the source.\nEverything is explained there: structure of keyManifest, tableManifest, filters, all operators, examples.\n\n\n\n\u003cbr\u003e\u003cbr\u003e\n◤◤◤ Testing \u0026 Contributing\n\nGot an idea, found a bug, or just want to say thanks?\nOpen an issue or a pull request – we appreciate any feedback.\n\n\n\n\u003cbr\u003e\u003cbr\u003e\n◤◤◤ License\n\nwaveQl is released under the MIT license.\nYou are free to use, modify, and distribute it – even in commercial projects. A little shoutout would be nice, but it's not required.\n\nTry it now and fall in love with filtering! 💙\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fe2see%2Fwaveql","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fe2see%2Fwaveql","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fe2see%2Fwaveql/lists"}