xwero / idable-queries-core
base package for Idable queries libraries
Installs: 14
Dependents: 1
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/xwero/idable-queries-core
Requires
- php: >=8.4
Requires (Dev)
- pestphp/pest: 4.x-dev
README
This package provides the base functions and types for the database specific packages.
The main part of this package functions are helpers to work with the Identifier instances.
Overview
Idable queries are a set of packages that are at the core a multi-database wrapper.
While you can use it as a wrapper with the same functions and types per database, the libraries provide an abstraction for the database names and parameters.
At the base of the abstraction is the Identifier interface. It is nothing more than a library specific name for enums.
enum Users implements Indentifier
{
case Users; // It is recommended to use enun name for the table/collection/set/... to make the identifier more universal
case Name;
}
A backed enum is recommended to separate database name from the enum name.
A SQL query, SELECT name FROM users WHERE name = 'me';, can now be written as SELECT ~Users:Name FROM ~Users:Users WHERE ~Users:Name = :Users:Name;.
A Redis query like HMSET users name "Hello" email "World" can be written as HMSET ~Users:Users ~Users:Name :Users:Name ~Users:Email :Users:Email.
And so on with other database types.
note: The placeholders are case-insensitive, but for the best compilation path in PHP it is recommended to use capitals.
To make it easier to add multiple parameters an CustomParameterIdentifier attribute can be added to an Identifier instance.
This signals to the parameter functions that the parameter value will use a transformer to replace a single placeholder.
#[CustomParameterIdentfier('Xwero\IdableQueriesRedis\setParameterTransformer')]
enum Set implements Identfier
{
case Users;
}
Now the Redis query can be written as HMSET ~Users:Users :Set:Users.
And depending on the values in the IdableParameterCollection instance the query will be changed.
Each database package will have predefined transformer functions. Creating the Identifier instance is up to the implementers.
The libraries will have map functions that transform the result of the query in an array-like structure where the key is an Identifier instance.
As an example the SELECT ~Users:Name FROM ~Users:Users WHERE ~Users:Name = :Users:Name; query result called with the createMapFromFirstLevelResults function will have the $map[Users:Name] value me.
This prevents typos when using the results.
The libraries are build with utility in mind. That is why the main functionality is in the functions, rather than in objects. Use as much or as little as you like.
Functions
addIdableParameters
This is the more dangerous version of the collectIdableParameters function, as it adds the values to the query without preparation.
addIdableParameters('SELECT * FROM ~Users:Users WHERE ~Users:Name = :Users:Name;', IdableParameterCollection::createWithItem(User::Name, 'me')) results in SELECT * FROM ~Users:Users WHERE ~Users:Name = me
Used by the database system packages.
buildAliasMap
This function allows full control over the creation of a Map instance.
The data needs to be an associate array.
used by buildAliasMapCollection
buildAliasMapCollection
Helper function to transform a two-dimensional data array into an array of Map instances.
buildLevelMap
Helper function to create a Map instance from an associate array.
Used by createMapFromFirstLevelResults and createMapFromSecondLevelResults.
collectIdableParameters
This function allows the application code to prepare the parameters before adding it to the query.
Used by replaceParametersInQuery.
createMapFromFirstLevelResults
Function with extra checks on top of buildLevelMap.
note: This is more robust function, and should be the one used in a function chain.
Used by the database system packages.
createMapFromSecondLevelResults
When the query returns multiple items use this function to create a MapCollection.
Used by the database system packages.
fillMapWithAliases
A helper method for buildAliasMap and buildLevelMap to add data to the Map based on the added aliases.
getIdentifierFromStrings
This function accepts the class and case as a string and insures the return is either null or an identifier.
It is possible to add a number to the case in the occasion the same identifier needs to be used more than once.
As an example WHERE ~Users:Name = :Users:Name1 AND ~Users:Name != :Users:Name2
Used by queryToPlaceholderCollection.
getIdentifierRegex
This function gets the default regex or a custom one from the IDENTIFIER_REGEX environment variable.
getParameterRegex
This function get the default regex or a custom one from the PARAMETER_REGEX environment variable.
note: Both regexes should have a colon as divider between the class and the case.
isIdableQuery
Check to verify if a query has placeholders that can be picked up by the idable regexes.
queryStringFromIdentifier
Gets a backed enum value or a lower string basic enum name.
Used by buildlevelmap and PlaceholderReplacementCollection.
queryToPlaceholderCollection
This function extracts the strings that match the identifier regex and makes identifiers from the strings.
An error is returned when placeholders are found but the collections is empty. the most likely cause is a wrong namespace. It is not possible to return an error when only one or a few namespaces are wrong. To prevent unexpected results it is best to use the namespaces argument when the functions provide it.
note: The collection is sorted from largest string size to smallest string size of the placeholders. This is to prevent bad replacements.
Used by addQueryParameters, collectQueryParameters, createMapFromFirstLevelResults, createMapFromSecondLevelResults and replaceIdentifiersInQuery.
replaceIdentifiersInQuery
Replaces the identifier placeholders from the query with the database names.
Used by the database packages.
replaceParametersInQuery
Replaces the parameter placeholders where the data isn't matched by the query placeholders.
:Arr:Test will become (:Arr:Test_0,:Arr:Test_1) when used in the PDO package.
The number of placeholders is based on the data in the IdableParameterCollection.
A $placeholderTransformer closure can be added to make sure the placeholders don't contain characters the database doesn't recognize.
Returns a DirtyQuery instance or false when there are no query changes.
Used by the database packages.
runChain
When using a PHP version under 8.5, this function can be used to have a similar experience than with the pipe operator.
$query = 'SELECT ~Users:Name, ~Users:Email FROM ~Users:Users WHERE ~Users:Id = :UsersId';
$namespaces = new BaseNamespaceCollection('Test\Identifiers');
$result = replaceIdentifiersInQuery($query, $namespaces)
|> (fn($query) => replaceParametersInQuery($query, new IdableParameterCollection(Users::Id, 1), $namespaces))
|> (fn($queryAndParameters) => someDatabaseFunction($queryAndParameters))
|> (fn($data) => createMapFromSecondLevelResults($data, $query, namespaces: $namespaces))
;
// with runChain
$query = 'SELECT ~Users:Name, ~Users:Email FROM ~Users:Users WHERE ~Users:Id = :UsersId';
$namespaces = new BaseNamespaceCollection('Test\Identifiers');
$result = runChain(new Chain(
fn() => replaceIdentifiersInQuery($query, $namespaces)
fn($query) => replaceParametersInQuery($query, new IdableParameterCollection(Users::Id, 1), $namespaces),
fn($queryAndParameters) => someDatabaseFunction($queryAndParameters),
fn($data) => createMapFromSecondLevelResults($data, $query, namespaces: $namespaces)
));
Types
Most of the collection types are added to make sure the content is of a certain type.
Alias
A data transfer object that holds the information to be used by the functions that use the AliasCollection.
AliasCollection
It is used to match aliases in the buildAliasMap, buildAliasesMapCollection, buildLevelMap, createMapFromFirstLevelResults and createMapFromSecondLevelResults functions.
The constructor only accepts Alias instances.
It has the methods:
- createWithItem: a static convenience method for
new AliasCollection()->add(). - add: this method uses the
Aliasconstructor arguments to create an instance. This method can return anErroror the instance. - getAlias: finds the matching
Aliasinstance by a string value.
It extends BaseCollection.
BaseCollection
Has a single array to contain the collection items. Can be used for collections with a single type.
It has the methods:
- getAll: returns array
- isEmpty: checks the array size
Chain
Used to add closures to the runChain function.
The constructor only accepts Closure values.
Extends BaseCollection.
CustomParameterIdentifier
The class attribute to add to an identifier to allow custom parameter transformations.
Database specific packages have data transformer functions to add to the attribute.
DirtyQuery
A DTO that is instantiated in replaceParametersInQuery when a query needs parameter replacements.
Error
The catch-all exception type to make it easier to let them pass through a function chain.
ExecutablePair
Exposes the changed query and the placeholders that match the changed query.
IdableParameter
The name is chosen to differentiate between parameter placeholders that use Identifier cases, and the native language parameters.
The latter are most of the time added as an array to the functions.
To allow query placeholders with a trailing number this class is created get the value by Identifier and number.7
An example is new IdableParameter(Users::Name, 'me', 1).
IdableParameterCollection
Used to add values to the parameter identifier placeholders in the query.
:Users:name is replaced by me when executing the query with the collection instance,
IdableParameterCollection::createWithItem(Users::Name, 'me').
The constructor accepts IdableParameter instances.
It has the methods:
- createWithItem: a static convenience for
new IdableParameterCollection()->add() - add: this uses the same arguments as the
IdableParameterconstructor to create an instance, and add it to the collection - findValueByIdentifierAndPlaceholder: used by the
addIdableParametersandcollectIdableParametersfunctions
Extends BaseCollection.
Identifier
The interface all the enums must implement to be used as placeholders in the queries and keys in maps.
A backed enum is recommended because it provides the best configuration. When it is a basic enum the case name will be lowercased to replace the placeholders.
JSONError
A convenience class were the message needs to added to create an Error instance with and instance of the JSONException as the exception.
JSONException
The message you add to the exception is appended with the JSON error message from PHP.
Map
Added to make the language more consistent.
MapCollection
The constructor only allows Map instances.
It has the method:
- addMap: appends a
Mapinstance to the collection.
Extends BaseCollection.
NamespaceCollection
It is used to make the identifier placeholders in the queries shorter.
The constructor only accepts string values.
Extends BaseCollection.
PlaceHolder
This class binds the placeholder with the identifier, and optionally the value.
The class required properties are placeholder and identifier.
The value property is used for collecting the data that a function passes on.
The prefix and suffix properties are used to manipulate the placeholder return.
It has the methods:
- getFullPlaceholder: which concatenates the
prefix,placeholderandsuffixproperty values. - getCustomValue: when the ´identifier´ value has a
CustomParameterIdentifierattribute, this method will execute the data transformer function
PlaceHolderCollection
The constructor only accepts PlaceHolder instances.
It has the methods:
- createWithPlaceholderItem: a static convenience with for
new Placeholder()->add() - add: uses the same arguments as the
PlaceHolderconstructor to create an instance and add it to the collection. - getPlaceholderReplacements: returns a flattened array in case the placeholders in the query need to be replaced.
- getPlaceholdersAsText: calls the
getFullPlaceholderon every item of the collection to create a string. - getPlaceholderValuePairs: returns a flattened array with the placeholders and matching values
Extends BaseCollection.
QueryReturnConfig
The interface for database specific output configuration.
Statement
The interface to make the Statement classes consistent in the database specific packages.
Tips
- Use the namespaces argument of the functions to prevent unexpected results because of typos or changed namespaces.