{"id":22344233,"url":"https://github.com/hi-folks/data-block","last_synced_at":"2026-01-04T12:12:59.119Z","repository":{"id":244571235,"uuid":"815452535","full_name":"Hi-Folks/data-block","owner":"Hi-Folks","description":"PHP Package for handling, querying, filtering, and setting nested data structures","archived":false,"fork":false,"pushed_at":"2025-03-22T19:24:07.000Z","size":219,"stargazers_count":73,"open_issues_count":1,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-29T11:11:15.493Z","etag":null,"topics":["data","data-structure","hacktoberfest","json-data","php"],"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/Hi-Folks.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":".github/SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-06-15T07:46:39.000Z","updated_at":"2025-03-22T19:22:28.000Z","dependencies_parsed_at":"2025-01-06T11:25:20.525Z","dependency_job_id":"cda2fdbd-a730-40d8-82ee-62df3fe5412c","html_url":"https://github.com/Hi-Folks/data-block","commit_stats":null,"previous_names":["hi-folks/data-block"],"tags_count":24,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hi-Folks%2Fdata-block","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hi-Folks%2Fdata-block/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hi-Folks%2Fdata-block/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hi-Folks%2Fdata-block/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Hi-Folks","download_url":"https://codeload.github.com/Hi-Folks/data-block/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247332609,"owners_count":20921853,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["data","data-structure","hacktoberfest","json-data","php"],"created_at":"2024-12-04T09:09:13.258Z","updated_at":"2026-01-04T12:12:59.112Z","avatar_url":"https://github.com/Hi-Folks.png","language":"PHP","readme":"\u003cp align=\"center\"\u003e\n    \u003cimg src=\"https://raw.githubusercontent.com/Hi-Folks/data-block/main/cover-data-block.png\" alt=\"PHP Data Block package\"\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003e\n    Data Block Package\n\u003c/h1\u003e\n\n\n\u003cp align=center\u003e\n    \u003ca href=\"https://packagist.org/packages/hi-folks/data-block\"\u003e\n        \u003cimg src=\"https://img.shields.io/packagist/v/hi-folks/data-block.svg?style=for-the-badge\" alt=\"Latest Version\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://packagist.org/packages/hi-folks/data-block\"\u003e\n        \u003cimg src=\"https://img.shields.io/packagist/dt/hi-folks/data-block.svg?style=for-the-badge\" alt=\"Total Downloads\"\u003e\n    \u003c/a\u003e\n    \u003cbr /\u003e\n    \u003cimg src=\"https://img.shields.io/packagist/l/hi-folks/data-block?style=for-the-badge\" alt=\"Packagist License\"\u003e\n    \u003cimg src=\"https://img.shields.io/packagist/php-v/hi-folks/data-block?style=for-the-badge\" alt=\"Supported PHP Versions\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/last-commit/hi-folks/data-block?style=for-the-badge\" alt=\"GitHub last commit\"\u003e\n    \u003cbr /\u003e\n        \u003cimg src=\"https://img.shields.io/github/actions/workflow/status/hi-folks/data-block/run-tests.yml?style=for-the-badge\u0026label=Test\" alt=\"Tests\"\u003e\n\u003c/p\u003e\n\n\n\u003cp align=center\u003e\n    \u003ci\u003e\n        PHP Package for Managing Nested Data\n    \u003c/i\u003e\n\u003c/p\u003e\nThis PHP package provides classes and methods for easily managing, querying, filtering, and setting nested data structures.\nThe PHP Data Block package offers a streamlined approach to handling nested data, whether you're working with complex JSON data, hierarchical configurations, or deeply nested arrays.\n\n## What you can do with PHP Data Block\n\nFor example, with PHP Data Block, you can retrieve complex JSON from an API and then filter, sort, and handle the data.\nFor example, here you can:\n\n- Retrieve the list of the repository from GitHub\n- Order the repository by the number of stars received (ascending, starting from the repos with more stars)\n- Loop the results\n- Get values from elements\n\n```php\n\u003c?php\n\nuse HiFolks\\DataType\\Block;\n\nrequire './vendor/autoload.php';\n$url = 'https://api.github.com/orgs/hi-folks/repos';\n\nBlock::fromJsonUrl($url)\n    -\u003eorderBy('stargazers_count', 'desc')\n    -\u003eforEach(\n        function ($item) {\n            echo $item-\u003eget('full_name').' : ';\n            echo $item-\u003eget('stargazers_count').PHP_EOL;\n        }\n    );\n```\n\nThen, you can do more, you can:\n- Extract only the fields you need using the `select()` method\n- Filter the elements with the `where()` method and using the `Operator` class\n\nHere is an example:\n\n```php\n\u003c?php\n\nuse HiFolks\\DataType\\Block;\nuse HiFolks\\DataType\\Enums\\Operator;\n\nrequire './vendor/autoload.php';\n$url = 'https://api.github.com/orgs/hi-folks/repos';\n\nBlock::fromJsonUrl($url)\n    -\u003eselect('full_name', 'stargazers_count')\n    -\u003ewhere('stargazers_count', Operator::GREATER_THAN, 0)\n    -\u003eorderBy('stargazers_count', 'desc')\n    -\u003eforEach(\n        function ($item) {\n            echo $item-\u003eget('full_name').' : ';\n            echo $item-\u003eget('stargazers_count').PHP_EOL;\n        }\n    );\n```\nThis is just an overview as an appetizer :)\n\nNow, let's explore the classes and methods PHP Data Block provides.\n\nOne \"core\" element of the PHP Data Block package is the Block PHP class.\n\n## The Block class\n\nThe **Block** class offers comprehensive methods to create, manage, and access nested data structures.\n\nThe **Block** class provides various methods, including:\n\n- Creating structures from Arrays, JSON, and YAML files.\n- Querying nested data with ease.\n- Filtering data based on specific criteria.\n- Setting and updating values within deeply nested structures.\n\n## Installing and using the Block class\n\nFor adding to your projects, the Block class with its methods and helpers, you can run the `composer require` command:\n\n```php\ncomposer require hi-folks/data-block\n```\n\n\u003e To support the development, you can \"star\" ⭐ the repository: https://github.com/Hi-Folks/data-block\n\nThen, in your PHP files, you can import the `HiFolks\\DataType\\Block` Namespace:\n\n```php\nuse HiFolks\\DataType\\Block;\n```\n\n## Method for creating Block objects\nTo show the capabilities of the following methods, I will use this nested associative array:\n\n```php\n$fruitsArray = [\n    \"avocado\" =\u003e\n    [\n        'name' =\u003e 'Avocado',\n        'fruit' =\u003e '🥑',\n        'wikipedia' =\u003e 'https://en.wikipedia.org/wiki/Avocado',\n        'color'=\u003e'green',\n        'rating' =\u003e 8\n    ],\n    \"apple\" =\u003e\n    [\n        'name' =\u003e 'Apple',\n        'fruit' =\u003e '🍎',\n        'wikipedia' =\u003e 'https://en.wikipedia.org/wiki/Apple',\n        'color' =\u003e 'red',\n        'rating' =\u003e 7\n    ],\n    \"banana\" =\u003e\n    [\n        'name' =\u003e 'Banana',\n        'fruit' =\u003e '🍌',\n        'wikipedia' =\u003e 'https://en.wikipedia.org/wiki/Banana',\n        'color' =\u003e 'yellow',\n        'rating' =\u003e 8.5\n    ],\n    \"cherry\" =\u003e\n    [\n        'name' =\u003e 'Cherry',\n        'fruit' =\u003e '🍒',\n        'wikipedia' =\u003e 'https://en.wikipedia.org/wiki/Cherry',\n        'color' =\u003e 'red',\n        'rating' =\u003e 9\n    ],\n];\n```\n\n### The static `make()` method\nWith the static `make()` method, you can generate a Block object from an associative array:\n```php\n$data = Block::make($fruitsArray);\n```\nThe `$data` object is an instance of the `Block` class.\n\nIn the case you want to initialize an empty `Block` object, you can call the `make()` method with no parameters:\n```php\n$data = Block::make();\n```\nOnce you initialize the Block object, you can use its methods.\n\n### The `get()` method\nThe `get()` method supports keys/indexes with the dot (or custom) notation for retrieving values from nested arrays.\nIt returns the original type of data. If you need to obtain a Block object, you should use the `getBlock()` method instead of `get()`.\nFor example:\n\n```php\n$data-\u003eget('avocado'); // returns an array\n$data-\u003eget('avocado.color'); // returns the string \"green\"\n```\n\nFor example, with the `$fruitsArray` sample data, the `$data-\u003eget(\"avocado\")` is:\n- an array;\n- has five elements;\n\nFor example, the `$data-\u003eget(\"avocado.color\")` is:\n- a string;\n- has the value \"green\";\n\nThe `$data-\u003eget(\"avocado.rating\")` is:\n- a numeric;\n- specifically an integer;\n\nThe `$data-\u003eget(\"banana.rating\")` is:\n- a numeric;\n- specifically a float;\n\n\nYou can customize the notation with a different character:\n\n```php\n$data-\u003eget('apple#fruit', charNestedKey: '#'); // 🍎\n```\n\nIf you are going to access a not valid key, a `null` value is returned:\n\n```php\n$value = $data-\u003eget('apple.notexists'); // null\n```\nYou can define a default value in the case the key doesn't exist:\n\n```php\n$value = $data-\u003eget(\n    'apple.notexists',\n    '🫠'\n); // 🫠\n```\n\nAnd you can combine the default value and the nested character:\n```php\n$value = $data-\u003eget(\n    'apple#notexists',\n    '🫠',\n    '#'\n); // 🫠\n```\n\n### The `getFormattedDateTime()` for getting and formatting a date-time field value\n\nWhen working with date-time fields, consider utilizing the `getFormattedDateTime()` method instead of relying solely on `get()`. This approach not only retrieves the value but also formats it according to the specified date-time format, as defined by the second optional parameter `$format`.\n\nBy default, this formatting is set to \"Y-m-d H:i:s\", providing a convenient and standardized output. However, customizing the format allows for more flexibility in presenting your data.\n\nHere are some key points:\n\n*   `getFormattedDateTime()` combines the functionality of `get()` with date-time formatting.\n*   The second parameter `$format` controls the date-time format used for formatting.\n*   Custom formats can be applied to provide tailored outputs for different use cases.\n\n### The `getFormattedByte()` for getting and formatting a 'byte' field value\n\nThe `getFormattedByte()` method retrieves and formats a byte value from a specified field in a data block. It converts the raw byte count into a more human-readable format (e.g., GB, MB, etc.), with an optional precision parameter to control the number of decimal places displayed.\n\nParameters:\n- `$path` (string): The path to the field containing the byte value (e.g., \"assets.0.total_bytes\").\n- `$precision` (int): (Optional) Number of decimal places to include in the formatted result. The default is 2.\n\nExample usage:\n\n```php\n$data1-\u003egetFormattedByte(\"assets.0.total_bytes\");  // Returns \"5.98 GB\"\n$data1-\u003egetFormattedByte(\"assets.1.total_bytes\");  // Returns \"2.18 GB\"\n$data1-\u003egetFormattedByte(\"assets.1.total_bytes\", 5);  // Returns \"2.18288 GB\"\n$data1-\u003egetFormattedByte(\"assets.1.total_bytes\", 0);  // Returns \"2 GB\"\n```\n\nKey Features:\n- Automatic unit conversion: converts bytes into appropriate units (e.g., KB, MB, GB) based on the size.\n- Customizable precision: you can specify the number of decimal places for the output, making it flexible for various use cases.\n\n### The `getString()` method\n\nThe `getString()` method retrieves the value of a specified field as a string from a data block. If the field does not exist or is null, it returns a default value, which can be customized.\n\nParameters:\n\n- `$path` (string): The path to the field (e.g., \"0.commit.author.date\").\n- `$default` (string|null): (Optional) The default value to return if the field doesn't exist. Defaults to null.\n\nExample Usage:\n```php\n$data1-\u003egetString(\"0.commit.author.date\");  // Returns the field value as a string\n$data1-\u003egetString(\"0.commit.author.notexists\");  // Returns null\n$data1-\u003egetString(\"0.commit.author.notexists\", \"AA\");  // Returns \"AA\"\n$data1-\u003egetString(\"0.commit.comment_count\");  // Returns \"0\" as a string even if the field value is an integer\n```\n\n### The `getStringStrict()` method\n\nThe `getStringStrict()` method retrieves the value of a specified field as a string from a data block. If the field does not exist or is null, it returns a default string value, which can be customized (\"\" by default).\n\nParameters:\n\n- `$path` (string): The path to the field (e.g., \"0.commit.author.date\").\n- `$default` (string): (Optional) The default value to return if the field doesn't exist. Defaults to \"\".\n\nExample Usage:\n```php\n$data1-\u003egetStringStrict(\"0.commit.author.date\");  // Returns the field value as a string\n$data1-\u003egetStringStrict(\"0.commit.author.notexists\");  // Returns an empty string \"\"\n$data1-\u003egetStringStrict(\"0.commit.author.notexists\", \"AA\");  // Returns \"AA\"\n$data1-\u003egetStringStrict(\"0.commit.comment_count\");  // Returns \"0\" as a string even if the field value is an integer\n```\n\n### The `getInt()` method\n\nThe `getInt()` method retrieves the value of a specified field as a integer from a data block. If the field does not exist or is null, it returns a default value, which can be customized (null by default).\nParameters:\n\n- `$path` (string): The path to the field (e.g., \"0.author.id\").\n- `$default` (null|int): (Optional) The default value to return if the field doesn't exist. Defaults to null.\n- `$charNestedKey` (string): the character separator for nested field names.  The default is \".\".\n\nExample usage:\n```php\n$data1-\u003egetInt(\"0.author.id\"); // Returns the field value as an integer, for example 678434\n$data1-\u003egetInt(\"0.author.idx\"); // Returns null because the field doesn't exists\n$data1-\u003egetInt(\"0.author.idx\", 44); // Returns 44 because the field doesn't exists, and you set a default, in this case 44\n```\n\n### The `getIntStrict()` method\n\nThe `getIntStrict()` method retrieves the value of a specified field as a integer from a data block. If the field does not exist or is null, it returns a default value, which can be customized (0 by default).\nParameters:\n\n- `$path` (string): The path to the field (e.g., \"0.author.id\").\n- `$default` (int): (Optional) The default value to return if the field doesn't exist. Defaults to 0.\n- `$charNestedKey` (string): the character separator for nested field names.  The default is \".\".\n\nExample usage:\n```php\n$data1-\u003egetIntStrict(\"0.author.id\"); // Returns the field value as an integer, for example 678434\n$data1-\u003egetIntStrict(\"0.author.idx\"); // Returns 0 because the field doesn't exists, and the method is strict\n$data1-\u003egetIntStrict(\"0.author.idx\", 44); // Returns 44 because the field doesn't exists, and you set a default, in this case 44\n```\n\n### The `getFloat()` method\n\nThe `getFloat()` method retrieves the value of a specified field as a float from a data block. If the field does not exist or is null, it returns a default value, which can be customized (`null` by default).\n\n**Parameters:**\n\n- `$path` (string): The path to the field (e.g., `\"0.author.score\"`).\n- `$default` (null|float): (Optional) The default value to return if the field doesn't exist. Defaults to null.\n- `$charNestedKey` (string): The character separator for nested field names. The default is `\".\"`.\n\n**Example usage:**\n\n```php\n$data1-\u003egetFloat(\"0.author.score\"); // Returns the field value as a float, for example 4.75\n$data1-\u003egetFloat(\"0.author.notexists\"); // Returns null because the field doesn't exist\n$data1-\u003egetFloat(\"0.author.notexists\", 1.5); // Returns 1.5 because the field doesn't exist, and you set a default\n```\n\n### The `getFloatStrict()` method\n\nThe `getFloatStrict()` method retrieves the value of a specified field as a float from a data block. If the field does not exist or is null, it returns a default value, which can be customized (`0.0` by default).\n\n**Parameters:**\n\n- `$path` (string): The path to the field (e.g., `\"0.author.score\"`).\n- `$default` (float): (Optional) The default value to return if the field doesn't exist. Defaults to `0.0`.\n- `$charNestedKey` (string): The character separator for nested field names. The default is `\".\"`.\n\n**Example usage:**\n\n```php\n$data1-\u003egetFloatStrict(\"0.author.score\"); // Returns the field value as a float, for example 4.75\n$data1-\u003egetFloatStrict(\"0.author.notexists\"); // Returns 0.0 because the field doesn't exist, and the method is strict\n$data1-\u003egetFloatStrict(\"0.author.notexists\", 1.5); // Returns 1.5 because the field doesn't exist, and you set a default\n```\n\n### The `getBoolean()` method\n\nThe `getBoolean()` method retrieves the value of a specified field as a boolean from a data block. If the field does not exist or is null, it returns a default value, which can be customized (null by default).\nParameters:\n\n- `$path` (string): The path to the field (e.g., \"0.author.id\").\n- `$default` (null|bool): (Optional) The default value to return if the field doesn't exist. Defaults to null.\n- `$charNestedKey` (string): the character separator for nested field names.  The default is \".\".\n\nExample usage:\n```php\n$data1-\u003egetBoolean(\"0.author.site_admin\"); // Returns the field value as an boolean, for example true\n$data1-\u003egetBoolean(\"0.author.site_admin_notexists\"); // Returns null because the field doesn't exists\n$data1-\u003egetBoolean(\"0.author.site_admin_notexists\", true); // Returns true because the field doesn't exists, and you set a default, in this case true\n```\n\n### The `getBooleanStrict()` method\n\nThe `getBooleanStrict()` method retrieves the value of a specified field as a boolean from a data block. If the field does not exist or is null, it returns a strict boolean default value, which can be customized (false by default).\nParameters:\n\n- `$path` (string): The path to the field (e.g., \"0.author.id\").\n- `$default` (bool): (Optional) The default value to return if the field doesn't exist. Defaults to false.\n- `$charNestedKey` (string): the character separator for nested field names.  The default is \".\".\n\nExample usage:\n```php\n$data1-\u003egetBooleanStrict(\"0.author.site_admin\"); // Returns the field value as an boolean, for example true\n$data1-\u003egetBooleanStrict(\"0.author.site_admin_notexists\"); // Returns false because the field doesn't exists\n$data1-\u003egetBooleanStrict(\"0.author.site_admin_notexists\", true); // Returns true because the field doesn't exists, and you set a default, in this case true\n```\n\n\n### The `getBlock()` method\nIf you need to manage a complex array (nested array) or an array obtained from a complex JSON structure, you can access a portion of the array and obtain the `Block` object via the `getBlock()` method.\n\nLet's see an example:\n\n```php\n$appleData = $data-\u003egetBlock(\"apple\")\n// $data is the Block instance so that you can access\n// to the Block methods like count()\n$data-\u003egetBlock(\"apple\")-\u003ecount();\n```\n\nIf the element accessed via `getBlock()` is a scalar type (integer, float, string, etc.), a Block object (with just one element) will be returned using `getBlock()`.\n\nFor example, `$data-\u003egetBlock(\"avocado\")` returns a Block object with five elements.\n\nFor example, `$data-\u003egetBlock(\"avocado.color\")` returns a Block object with just one element.\n\nIf you are going to access a non-valid key, an empty Block object is returned, so the `$data-\u003egetBlock(\"avocado.notexists\")` returns a Block object with a length equal to 0.\n\n### The `set()` method\nThe `set()` method supports keys with the dot (or custom) notation for setting values for nested data.\nIf a key doesn't exist, the `set()` method creates one and sets the value.\nIf a key already exists, the `set()` method will replace the value related to the key.\n\n#### Parameters\n\n- `key` (int|string): The key to which the value should be assigned. If a string is provided, you can use dot notation to set nested values.\n- `value` (mixed): The value to be assigned to the specified key.\n- `charNestedKey` (string, optional): The character used for dot notation in nested keys. Defaults to `.`.\n\n#### Returns\n\n- `self`: Returns the instance of the class for method chaining.\n\n#### Example Usage\n\n```php\n$articleText = \"Some words as a sample sentence\";\n$textField = Block::make();\n$textField-\u003eset(\"type\", \"doc\");\n$textField-\u003eset(\"content.0.content.0.text\", $articleText);\n$textField-\u003eset(\"content.0.content.0.type\", \"text\");\n$textField-\u003eset(\"content.0.type\", \"paragraph\");\n```\n\nSo when you try to set a nested key as \"content.0.content.0.text\", it will be created elements as a nested array.\n\nOnce you set the values, you can access them via `get()` (or `getBlock()`) methods:\n\n```php\n$textField-\u003eget(\"content.0.content.0.text\");\n```\n\n### Extracting Keys\n\nVia the `keys()` method, you can retrieve the list of the keys:\n\n```php\n$data = Block::make($fruitsArray);\n$keys = $data-\u003ekeys();\n/*\nArray\n(\n    [0] =\u003e avocado\n    [1] =\u003e apple\n    [2] =\u003e banana\n    [3] =\u003e cherry\n)\n*/\n```\n\nYou can retrieve the keys of a nested element, combining the usage of `getBlock()` and the `keys()`:\n\n```php\n$data = Block::make($fruitsArray);\n$keys = $data-\u003egetBlock(\"avocado\")-\u003ekeys();\n\n/*\nArray\n(\n    [0] =\u003e name\n    [1] =\u003e fruit\n    [2] =\u003e wikipedia\n    [3] =\u003e color\n    [4] =\u003e rating\n)\n*/\n```\n\n## Exporting data\n\n### Exporting to array with `toArray()`\nThe `toArray()` method can access the native array (associative and nested).\n\nThis is helpful when manipulating data with the Block class, and at a certain point, you need to send the data to your function or a function from a third-party package that expects to receive a native array as a parameter.\n\n```php\n$file = \"./composer.json\";\n$composerContent = Block::fromJsonFile($file);\n// here you can manage $composerContent with Block methods\n// end then exports the Block data into a native array\n$array = $composerContent-\u003etoArray();\n```\n\n### Exporting to JSON string with `toJson()`\nIf you need to generate a valid JSON string using the content of the Block object, you can use the `toJson()` method.\n\nThis is helpful when you are manipulating data with the Block class and at a certain point need to send the data in JSON string format to your own function or a function from a third-party package that expects to receive a JSON string as a parameter.\n\n```php\n$data = Block::make($fruitsArray);\n$jsonString = $data-\u003etoJson(); // JSON string with \"pretty print\"\n```\n\n### Exporting to YAML string with `toYaml()`\nIf you need to generate a valid YAML string using the content of the Block object, you can use the `toYaml()` method.\n\nThis is helpful when manipulating data with the Block class and, at a certain point, need to send the data in YAML string format to your function or a function from a third-party package that expects to receive a YAML string as a parameter.\n\n```php\n$data = Block::make($fruitsArray);\n$yamlString = $data-\u003etoYaml(); // YAML string\n```\n\n### Saving JSON to a file with `saveToJson()`\nIf you need to save the JSON string in a file using the content of the Block object, you can use the `saveToJson()` method.\n\nThis is helpful when you are manipulating data with the Block class and at a certain point need to save the data in JSON string format to a file.\nThe `saveToJson()` method has two parameters:\n\n- `filename`: the first parameter (mandatory) with the filename;\n- `overwrite`: the second parameter (optional), If the file exists, the file is not saved by default, unless you set the overwrite parameter as true.\n\n```php\n$data = Block::make($fruitsArray);\n$jsonString = $data-\u003esaveToJson('./fruits.json', true);\n```\n\n\n## Loading Data\n\n### Loading Data from JSON file\n\n```php\n$file = \"./composer.json\";\n$composerContent = Block::fromJsonFile($file);\necho $composerContent-\u003eget(\"name\"); // for example: \"hi-folks/data-block\"\necho $composerContent-\u003eget(\"authors.0.name\"); // for example: \"Roberto B.\"\n```\n\n### Loading Data from JSON URL\n\nYou can build your Block data from a remote JSON (like an API).\nFor example, you can use the `fromJsonUrl()` method to build a Block object from the latest commits via GitHub API.\nRetrieving JSON API into a Block object is useful for applying the methods provided by the Block class, for example, filtering the data. In the example, I'm going to filter the commit based on the name of the author of the commit:\n\n```php\n$url = \"https://api.github.com/repos/hi-folks/data-block/commits\";\n$commits = Block::fromJsonUrl($url);\n$myCommits = $commits-\u003ewhere(\"commit.author.name\", Operator::LIKE, \"Roberto\");\nforeach ($myCommits as $value) {\n    echo $value-\u003eget(\"commit.message\") . PHP_EOL;\n}\n```\n\n### Loading Data from YAML file\n\n```php\n$file = \"./.github/workflows/run-tests.yml\";\n$workflow = Block::fromYamlFile($file);\necho $workflow-\u003eget(\"name\"); // Name of the GitHub Action Workflow\necho $workflow-\u003eget(\"jobs.test.runs-on\");\necho $workflow-\u003eget(\"on.0\"); // push , the first event\n```\n\n### Loading Data from JSON URL via Symfony HttpClient\n\nIf you want more control over the HTTP request (headers, authentication, timeouts, retries, etc.) or your environment restricts PHP stream functions (for example `allow_url_fopen=0`), you can use `fromHttpJsonUrl()`.  \nThis method relies on your Symfony HttpClient implementation and allows you to pass any request options supported by the client.\n\n```php\nuse HiFolks\\DataType\\Block;\nuse HiFolks\\DataType\\Enums\\Operator;\nuse Symfony\\Component\\HttpClient\\HttpClient;\n\n$url = \"https://api.github.com/repos/hi-folks/data-block/commits\";\n$client = HttpClient::create();\n\n$commits = Block::fromHttpJsonUrl($url, $client, [\n    'headers' =\u003e [\n        'User-Agent' =\u003e 'my-app',\n        'Accept' =\u003e 'application/json',\n    ],\n]);\n\n$myCommits = $commits-\u003ewhere(\"commit.author.name\", Operator::LIKE, \"Roberto\");\nforeach ($myCommits as $value) {\n    echo $value-\u003eget(\"commit.message\") . PHP_EOL;\n}\n```\nDon't forget to install the client's implementation `composer require symfony/http-client`\n\n\n## Adding and appending elements\n\n### Appending the elements of a Block object to another Block object\nIf you have a Block object, you can add elements from another Block object.\nOne use case is if you have multiple JSON files and want to retrieve paginated content from an API. In this case, you want to create one Block object with all the elements from every JSON file.\n\n```php\n$data1 = Block::fromJsonFile(\"./data/commits-10-p1.json\");\n$data2 = Block::fromJsonFile(\"./data/commits-10-p2.json\");\n$data1-\u003ecount(); // 10\n$data2-\u003ecount(); // 10\n$data1-\u003eappend($data2);\n$data1-\u003ecount(); // 20\n$data2-\u003ecount(); // 10\n```\n\n### Appending the elements of an array to a Block object\n\nIf you have an array, you can add elements to a Block object.\nUnder the hood, a Block object is an array (that potentially can be a nested array). Appending an array will add elements at the root level:\n\n\n```php\n$data1 = Block::make([\"a\",\"b\"]);\n$arrayData2 = [\"c\",\"d\"];\n$data1-\u003ecount(); // 2\n$data1-\u003eappend($arrayData2);\n$data1-\u003ecount(); // 4\n```\n\n### Appending an element\nIf you need to append an element as a single element (even if it is an array or a Block object), you can use the `appendItem()` function:\n\n```php\n$data1 = Block::make([\"a\", \"b\"]);\n$arrayData2 = [\"c\", \"d\"];\n$data1-\u003eappendItem($arrayData2);\n$data1-\u003ecount(); // 3 because a, b, and the whole array c,d as single element\n$data1-\u003etoArray();\n/*\n[\n    'a',\n    'b',\n    [\n        'c',\n        'd',\n    ],\n]\n*/\n```\n\n## Querying, sorting data\n\n### The `where()` method\nYou can filter data elements for a specific key with a specific value.\nYou can also set the operator\n\n```php\n$composerContent = Block::fromJsonString($jsonString);\n$banners = $composerContent-\u003egetBlock(\"story.content.body\")-\u003ewhere(\n    \"component\",\n    Operator::EQUAL,\n    \"banner\",\n);\n```\n\nWith the `where()` method, the filtered data keeps the original keys.\nIf you want to avoid preserving the keys and set new integer keys starting from 0, you can set the fourth parameter (`preserveKeys`) as `false`.\n\n```diff\n    $composerContent = Block::fromJsonString($jsonString);\n    $banners = $composerContent-\u003egetBlock(\"story.content.body\")-\u003ewhere(\n        \"component\",\n        Operator::NOT_EQUAL,\n        \"banner\",\n+        false\n    );\n```\n\nWith `where()` method you can use different operators, like \"==\", \"\u003e\", \"\u003c\" etc.\n\n\n\nYou can use also the `has` operator in the case your nested data contains arrays or `in` operator in the case you want to check if your data field value is included in an array of elements.\n\n#### The operators\n\nThe `Operator` class provides a set of predefined constants that represent comparison and logical operators. This ensures type safety and prevents errors from using invalid or misspelled operators in your data comparisons.\n\nSupported Operators:\n- `Operator::EQUAL` (==)\n- `Operator::GREATER_THAN` (\u003e)\n- `Operator::LESS_THAN` (\u003c)\n- `Operator::GREATER_THAN_OR_EQUAL` (\u003e=)\n- `Operator::LESS_THAN_OR_EQUAL` (\u003c=)\n- `Operator::NOT_EQUAL` (!=)\n- `Operator::STRICT_NOT_EQUAL` (!==)\n- `Operator::IN` (array inclusion)\n- `Operator::HAS` (array containment)\n- `Operator::LIKE` (string contains)\n\n\u003e The `Operator` class is defined in the `use HiFolks\\DataType\\Enums\\Operator` namespace.\n\n#### The `in` operator\n\nThe `in` operator is used within the where method to filter elements from a data collection based on whether a specific field's value exists within a given array of values.\nThe behavior is as follows:\n\n```php\n$data-\u003ewhere(\"field\", Operator::IN, [\"value1\", \"value2\", ...])\n```\n\nIf the field's value exists in the provided array, the element is included in the result.\nExample: Filtering fruits by color that match either \"green\" or \"black\"\n```php\n$greenOrBlack = $data-\u003ewhere(\"color\", Operator::IN, [\"green\", \"black\"]);\n```\n\nYou should use the `in` operator if your field is a scalar type (for example string or number) and you need to check if it is included in a list of values (array).\n\n#### The `has` operator\n\nThe `has` operator is used within the where method to filter elements from a data collection based on whether a specific field contains a given value, typically in cases where the field holds an array or a collection of tags or attributes. The behavior is as follows:\n\n```php\n$data-\u003ewhere(\"field\", Operator::HAS, \"value\")\n```\n\nFor example if you have posts and each post can have multiple tags, you can filter posts with a specific tag:\n\n```php\n$url = \"https://dummyjson.com/posts\";\n$posts = Block\n    ::fromJsonUrl($url)\n    -\u003egetBlock(\"posts\");\n\n$lovePosts = $posts-\u003ewhere(\"tags\", Operator::HAS, \"love\");\n```\n#### Summary `in` VS `has`\n\nThe `in` operator filters elements by matching a field's value against an array of possible values. If the value exists in the array, the element is included in the result. An empty array returns no results.\n\nThe `has` operator filters elements by checking if a specific value exists within a field (usually an array or a collection). If the value exists, the element is included in the result. Non-existent values return no matches.\n\n### The `extractWhere()` method\nThe `extractWhere()` method allows you to recursively query data elements and extract all elements that match a given property/value pair.\n\nIt is especially useful when working with deeply nested data structures (for example JSON content trees), where matching items may appear at any depth.\n\nThe implementation:\n- Recursively scans the entire Block\n- Finds all elements that:\n    - Contain the given $property\n    - Have a value strictly equal (===) to $value\n- Returns a new Block instance containing only the matched items\n- The original datablock is not modified\n\n```php\n$jsonString = file_get_contents('./tests/data/story.json');\n\n$story = Block::fromJsonString($jsonString);\n\n// Extract all items where \"fieldtype\" === \"asset\"\n$assets = $story-\u003eextractWhere('fieldtype', 'asset');\n\n// Debug output\n$assets-\u003edump();\n```\n\n### The `orderBy()` method\nYou can order or sort data for a specific key.\nFor example, if you want to retrieve the data at `story.content.body` key and sort them by `component` key:\n\n```php\n$composerContent = Block::fromJsonString($jsonString);\n$bodyComponents = $composerContent-\u003egetBlock(\"story.content.body\")-\u003eorderBy(\n    \"component\", \"asc\"\n);\n```\n\nYou can also order data for a nested attribute.\nConsider retrieving a remote JSON like the dummy JSON posts and then ordering the posts via the `reactions.likes` nested field in descending order:\n\n```php\nuse HiFolks\\DataType\\Block;\n\n$posts = Block\n    ::fromJsonUrl(\"https://dummyjson.com/posts\")\n    -\u003egetBlock(\"posts\");\necho $posts-\u003ecount(); // 30\n$mostLikedPosts = $posts-\u003eorderBy(\"reactions.likes\", \"desc\");\n$mostLikedPosts-\u003edump();\n```\n\n### The `select()` method\nThe `select()` method allows you to select only the needed fields.\nYou can list the field names you need as parameters for the `select()` method.\nFor example:\n\n```php\nuse HiFolks\\DataType\\Block;\n$dataTable = [\n    ['product' =\u003e 'Desk', 'price' =\u003e 200, 'active' =\u003e true],\n    ['product' =\u003e 'Chair', 'price' =\u003e 100, 'active' =\u003e true],\n    ['product' =\u003e 'Door', 'price' =\u003e 300, 'active' =\u003e false],\n    ['product' =\u003e 'Bookcase', 'price' =\u003e 150, 'active' =\u003e true],\n    ['product' =\u003e 'Door', 'price' =\u003e 100, 'active' =\u003e true],\n];\n$table = Block::make($dataTable);\n$data = $table\n    -\u003eselect('product' , 'price');\nprint_r($data-\u003etoArray());\n```\n\nYou can combine the `select()`, the `where()`, and the `orderBy()` method.\nIf you want to retrieve elements with `product` and `price` keys, with a price greater than 100 and ordered by `price`:\n\n```php\n$table = Block::make($dataTable);\n$data = $table\n    -\u003eselect('product' , 'price')\n    -\u003ewhere('price', Operator::GREATER_THAN, 100)\n    -\u003eorderBy(\"price\");\nprint_r($data-\u003etoArray());\n/*\nArray\n(\n    [0] =\u003e Array\n        (\n            [product] =\u003e Bookcase\n            [price] =\u003e 150\n        )\n\n    [1] =\u003e Array\n        (\n            [product] =\u003e Desk\n            [price] =\u003e 200\n        )\n\n    [2] =\u003e Array\n        (\n            [product] =\u003e Door\n            [price] =\u003e 300\n        )\n\n)\n*/\n```\n\n### The `groupBy()` method\n\nGroups the elements of the `Block` object by a specified field.\n\nThis method takes a field name as an argument and groups the elements of the `Block` object based on the values of that field. Each element is grouped into an associative array where the keys are the values of the specified field and the values are arrays of elements that share that key.\n\n```php\nuse HiFolks\\DataType\\Block;\n$data = Block::make([\n    ['type' =\u003e 'fruit', 'name' =\u003e 'apple'],\n    ['type' =\u003e 'fruit', 'name' =\u003e 'banana'],\n    ['type' =\u003e 'vegetable', 'name' =\u003e 'carrot'],\n]);\n$grouped = $data-\u003egroupBy('type');\n$grouped-\u003edumpJson();\n/*\n{\n    \"fruit\": [\n        {\n            \"type\": \"fruit\",\n            \"name\": \"apple\"\n        },\n        {\n            \"type\": \"fruit\",\n            \"name\": \"banana\"\n        }\n    ],\n    \"vegetable\": [\n        {\n            \"type\": \"vegetable\",\n            \"name\": \"carrot\"\n        }\n    ]\n}\n*/\n```\n\n### The `groupByFunction()` method\n\nThe `groupByFunction()` method allows you to group items from an Block based on a grouping logic provided by a callback function (closure). The function returns an associative array where the keys represent groupings defined by the callback, and the values are arrays of elements that belong to each group.\n\n```php\n$fruits = [\n    ['name' =\u003e 'Apple', 'type' =\u003e 'Citrus', 'quantity' =\u003e 15],\n    ['name' =\u003e 'Banana', 'type' =\u003e 'Tropical', 'quantity' =\u003e 10],\n    ['name' =\u003e 'Orange', 'type' =\u003e 'Citrus', 'quantity' =\u003e 8],\n    ['name' =\u003e 'Mango', 'type' =\u003e 'Tropical', 'quantity' =\u003e 5],\n    ['name' =\u003e 'Lemon', 'type' =\u003e 'Citrus', 'quantity' =\u003e 12]\n];\n$fruitsBlock = Block::make($fruits);\n$groupedByQuantityRange = $fruitsBlock-\u003egroupByFunction(\n    fn($fruit): string =\u003e\n        match (true) {\n            $fruit['quantity'] \u003c 10 =\u003e 'Low',\n            $fruit['quantity'] \u003c 15 =\u003e 'Medium',\n            default =\u003e 'High',\n        },\n);\n// It returns:\n/*\n{\n    \"High\": [\n        {\n            \"name\": \"Apple\",\n            \"type\": \"Citrus\",\n            \"quantity\": 15\n        }\n    ],\n    \"Medium\": [\n        {\n            \"name\": \"Banana\",\n            \"type\": \"Tropical\",\n            \"quantity\": 10\n        },\n        {\n            \"name\": \"Lemon\",\n            \"type\": \"Citrus\",\n            \"quantity\": 12\n        }\n    ],\n    \"Low\": [\n        {\n            \"name\": \"Orange\",\n            \"type\": \"Citrus\",\n            \"quantity\": 8\n        },\n        {\n            \"name\": \"Mango\",\n            \"type\": \"Tropical\",\n            \"quantity\": 5\n        }\n    ]\n}\n*/\n```\n\n### The `exists()` method\n\nYou can use the `exists()` method to check if an element that meets a certain condition exists. This method is a convenient way to determine if any records match your query without needing to count them explicitly.\n\nHere’s how you can use it:\n\n```php\n$has = $composerContent\n    -\u003egetBlock(\"story.content.body\")-\u003ewhere(\n        \"component\",\n        \"banner\",\n    )-\u003eexists();\n```\n\nThis will return true if a banner component exists, and false if it does not.\n\n## Looping Data\nThe Block class implements the Iterator interface.\nWhile looping an array via Block, by default, if the current element should be an array, a Block is returned so that you can access the Block method for handling the current array item in the loop.\nFor example, with the previous code, if you loop through `$data` (which is a Block object), each element in each iteration of the loop will be an array with two elements, with the keys `product` and `price`.\nIf in the loop you need to manage the current element via Block class, you should manually call the `Block::make`, for example:\n\n```php\n$table = Block::make($dataTable);\nforeach ($table as $key =\u003e $item) {\n    echo $item-\u003eget(\"price\");\n}\n```\n\nYou can apply filters and then loop into the result:\n\n```php\n$table = Block::make($dataTable);\n$data = $table\n    -\u003eselect('product', 'price')\n    -\u003ewhere('price', Operator::GREATER_THAN, 100, false);\nforeach ($data as $key =\u003e $item) {\n    echo $item-\u003eget(\"price\"); // returns an integer\n}\n\n```\n\n\nIf you want to loop through `$data` and obtain the current `$item` variable as an array you should set `false` as a second parameter in the static `make()` method:\n\n```php\n$table = Block::make($dataTable, false);\n$data = $table-\u003eselect('product', 'price')-\u003ewhere('price', Operator::GREATER_THAN, 100, false);\nforeach ($data as $key =\u003e $item) {\n    print_r($item); // $item is an array\n}\n```\n\n### The `iterateBlock()` method\nWith the `iterateBlock()` method, you can switch from array or Block for nested lists inside the main Block object if you already instanced it as a Block object.\nIn the example above, you have the `$table`  Block object.\nYou can loop across the items of the `$table` object.\nIf each item in the loop is itself an array (so an array of arrays), you can retrieve it as an array or a Block, depending on your needs:\n\n```php\n$table = Block::make($dataTable);\nforeach ($table as $key =\u003e $item) {\n    expect($item)-\u003etoBeInstanceOf(Block::class);\n    expect($key)-\u003etoBeInt();\n    expect($item-\u003eget(\"price\"))-\u003etoBeGreaterThan(10);\n}\n\n// iterateBlock(false if you need array instad of a nested Block)\nforeach ($table-\u003eiterateBlock(false) as $key =\u003e $item) {\n    expect($item)-\u003etoBeArray();\n    expect($key)-\u003etoBeInt();\n    expect($item[\"price\"])-\u003etoBeGreaterThan(10);\n}\n```\n\n### Using forEach() method\nThe `Block` class implements the `forEach()`method.\n\u003e If you need to walk through the `Block` object, you can use the `forEach()` method.\nYou can specify the function as an argument of the `forEach()` method to manage each single element.\n\n```php\n$url = \"https://dummyjson.com/posts\";\n$posts = Block::fromJsonUrl($url) // Load the Block from the remote URL\n    -\u003egetBlock(\"posts\") // get the `posts` as Block object\n    -\u003ewhere(\n        field:\"tags\",\n        operator: Operator::HAS,\n        value: \"love\",\n        preseveKeys: false,\n    ) // filter the posts, selecting only the posts with tags \"love\"\n    -\u003eforEach(fn($element): array =\u003e [\n        \"title\" =\u003e strtoupper((string) $element-\u003eget(\"title\")),\n        \"tags\" =\u003e count($element-\u003eget(\"tags\")),\n    ]);\n// The `$posts` object is an instance of the `Block` class.\n// The `$posts` object contains the items that matches the `where` method.\n// You can access to the elements via the nested keys\n// $posts-\u003eget(\"0.title\"); // \"HOPES AND DREAMS WERE DASHED THAT DAY.\"\n// $posts-\u003eget(\"0.tags\"); // 3\n```\n\n## Validating Data\n\nYou can validate the data in the Block object with JSON schema.\nJSON Schema is a vocabulary used to annotate and validate JSON documents.\n\n\u003e More info about JSON Schema: https://json-schema.org/learn/getting-started-step-by-step\n\nIf you need some common/popular schemas, you can find some schemas here: https://www.schemastore.org/json/\nFor example:\n\n- Schema for validating the `composer.json` file: https://getcomposer.org/schema.json\n- Schema for validating the GitHub Actions workflows: https://json.schemastore.org/github-workflow.json\n\nOr you can build your own schema according to the JSON schema specifications: https://json-schema.org/learn/getting-started-step-by-step#create-a-schema-definition\n\n\n```php\n$file = \"./.github/workflows/run-tests.yml\";\n$workflow = Block::fromYamlFile($file);\n$workflow-\u003evalidateJsonViaUrl(\n    'https://json.schemastore.org/github-workflow'\n    ); // TRUE if the Block is a valid GitHub Actions Workflow\n```\n\nOr you can define your own schema:\n\n```php\n$schemaJson = \u003c\u003c\u003c'JSON'\n{\n    \"type\": \"array\",\n    \"items\" : {\n        \"type\": \"object\",\n        \"properties\": {\n            \"name\": {\n                \"type\": \"string\"\n            },\n            \"fruit\": {\n                \"type\": \"string\"\n            },\n            \"wikipedia\": {\n                \"type\": \"string\"\n            },\n            \"color\": {\n                \"type\": \"string\"\n            },\n            \"rating\": {\n                \"type\": \"number\"\n            }\n        }\n    }\n}\nJSON;\n```\nAnd then validate it with your Block object:\n\n```php\n$fruitsArray = [\n    [\n        'name' =\u003e 'Avocado',\n        'fruit' =\u003e '🥑',\n        'wikipedia' =\u003e 'https://en.wikipedia.org/wiki/Avocado',\n        'color' =\u003e 'green',\n        'rating' =\u003e 8,\n    ],\n    [\n        'name' =\u003e 'Apple',\n        'fruit' =\u003e '🍎',\n        'wikipedia' =\u003e 'https://en.wikipedia.org/wiki/Apple',\n        'color' =\u003e 'red',\n        'rating' =\u003e 7,\n    ],\n    [\n        'name' =\u003e 'Banana',\n        'fruit' =\u003e '🍌',\n        'wikipedia' =\u003e 'https://en.wikipedia.org/wiki/Banana',\n        'color' =\u003e 'yellow',\n        'rating' =\u003e 8.5,\n    ],\n    [\n        'name' =\u003e 'Cherry',\n        'fruit' =\u003e '🍒',\n        'wikipedia' =\u003e 'https://en.wikipedia.org/wiki/Cherry',\n        'color' =\u003e 'red',\n        'rating' =\u003e 9,\n    ],\n];\n\n$data = Block::make($fruitsArray);\n$data-\u003evalidateJsonWithSchema($schemaJson);\n// true if the Block is valid.\n```\n\nIf you are starting to use the Data Block and testing it just to gain confidence, implementing different scenarios, or testing a non-valid JSON, try changing the \"rating\" type from number to integer (the validation should fail because in the JSON, we have ratings with decimals).\nAnd, yes, to change on the fly the schema you can use the Block object :)\n\n```php\n// load the schema as Block object...\n$schemaBlock = Block::fromJsonString($schemaJson);\n// so that you can change the type\n$schemaBlock-\u003eset(\n    \"items.properties.rating.type\",\n    \"integer\"\n);\n// the validation should be false because integer vs number\n$data-\u003evalidateJsonWithSchema(\n    $schemaBlock-\u003etoJson()\n);\n```\n\n## Applying functions\n\nThe `applyField()` method applies a callable function to the value of a specified field and sets the result to another field. This method supports method chaining.\n\n### Parameters\n\n- `key` (string|int): The key of the field whose value will be processed.\n- `targetKey` (string|int): The key where the result of the callable function should be stored.\n- `callable` (callable): The function to apply to the field value. This function should accept a single argument (the value of the field) and return the processed value.\n\n### Returns\n\n- `self`: Returns the instance of the class for method chaining.\n\n### Example Usage\n\n```php\n\u003c?php\n\n// Assuming $object is an instance of the class that contains the applyField method\n$object\n    -\u003eset('name', 'John Doe')\n    -\u003eapplyField('name', 'uppercase_name', function($value) {\n        return strtoupper($value);\n    });\n\necho $object-\u003eget('uppercase_name'); // Outputs: JOHN DOE\n\n## Testing\n\n```bash\ncomposer test\n```\n\n## Changelog\n\nPlease see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.\n\n## Contributing\n\nPlease see [CONTRIBUTING](.github/CONTRIBUTING.md) for details.\n\n## Security Vulnerabilities\n\nPlease review [our security policy](../../security/policy) on reporting security vulnerabilities.\n\n## License\n\nThe MIT License (MIT). Please see [License File](LICENSE.md) for more information.\n\n## Thanks to\n\nThank you to everyone who has provided feedback, opened issues, or created pull requests. A special thanks to all the contributors! You can view the full list of contributors [in this section](https://github.com/Hi-Folks/data-block/graphs/contributors).\n\nThe PHP ecosystem offers many tools that help developers enhance productivity, reliability, and efficiency. One such tool is JetBrains PhpStorm. JetBrains supports the open-source community by offering licenses for various open-source projects. More information can be found in the [Open Source section of the JetBrains website](https://jb.gg/OpenSourceSupport).\n\n\u003cimg src=\"https://resources.jetbrains.com/storage/products/company/brand/logos/PhpStorm_icon.png\" alt= \"PhpStorm logo\" width=\"128\" height=\"128\"\u003e\n\n\nI’m thrilled to share that JetBrains has provided an Open Source license for the PHP Data Block project.\nThis recognition of PHP Data Block as a valuable open-source software fills me with joy.\n\nThank you!\n\n[Roberto](https://github.com/roberto-butti)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhi-folks%2Fdata-block","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhi-folks%2Fdata-block","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhi-folks%2Fdata-block/lists"}