Skip to content

Commit

Permalink
Merge branch 'feature/second-release' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
cerbero90 committed Nov 29, 2023
2 parents f471ec6 + 73a7fb7 commit 63d402a
Show file tree
Hide file tree
Showing 44 changed files with 936 additions and 1,172 deletions.
2 changes: 1 addition & 1 deletion .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
github: cerbero90

ko_fi: cerbero90
52 changes: 22 additions & 30 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
name: build

on:
push:
pull_request:
push:
pull_request:

jobs:
tests:
Expand All @@ -11,20 +11,11 @@ jobs:
strategy:
fail-fast: false
matrix:
php: [7.2, 7.3, 7.4, 8.0]
laravel: [6.*, 7.*, 8.*]
php: [8.1, 8.2]
dependency-version: [prefer-lowest, prefer-stable]
os: [ubuntu-latest, windows-latest]
exclude:
- laravel: 6.*
php: 8.0
dependency-version: prefer-lowest
- laravel: 8.*
php: 7.2
- os: windows-latest
php: 8.0

name: PHP ${{ matrix.php }} - Laravel ${{ matrix.laravel }} - ${{ matrix.dependency-version }} - ${{ matrix.os }}
os: [ubuntu-latest]

name: PHP ${{ matrix.php }} - ${{ matrix.dependency-version }} - ${{ matrix.os }}

steps:
- name: Checkout code
Expand All @@ -34,17 +25,15 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: dom, curl, libxml, mbstring, zip
tools: composer:v2
coverage: none

- name: Install dependencies
run: |
composer require "illuminate/contracts=${{ matrix.laravel }}" --no-interaction --no-update
composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction
- name: Execute tests
run: vendor/bin/phpunit --verbose
run: vendor/bin/pest

coverage:
runs-on: ubuntu-latest
Expand All @@ -54,31 +43,30 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v2
with:
fetch-depth: 0

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 7.4
extensions: dom, curl, libxml, mbstring, zip
php-version: 8.1
tools: composer:v2
coverage: xdebug

- name: Install dependencies
run: composer update --prefer-stable --prefer-dist --no-interaction

- name: Execute tests
run: vendor/bin/phpunit --coverage-text --coverage-clover=coverage.clover
run: vendor/bin/pest --coverage-text --coverage-clover=coverage.clover

- name: Upload coverage
if: github.repository_owner == 'cerbero90'
run: |
wget https://scrutinizer-ci.com/ocular.phar
php ocular.phar code-coverage:upload --format=php-clover coverage.clover
vendor/bin/ocular code-coverage:upload --format=php-clover coverage.clover
style:
linting:
runs-on: ubuntu-latest

name: Coding style
name: Linting

steps:
- name: Checkout code
Expand All @@ -87,9 +75,13 @@ jobs:
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.0
tools: phpcs
php-version: ${{ matrix.php }}
tools: composer:v2
coverage: none

- name: Execute check
run: phpcs --standard=psr12 src/ tests/
- name: Install dependencies
run: |
composer update --prefer-stable --prefer-dist --no-interaction
- name: Execute Duster
run: vendor/bin/duster lint -u tlint,phpcodesniffer,pint,phpstan
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ vendor
phpcs.xml
phpunit.xml
.phpunit.result.cache
.DS_Store
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Updates should follow the [Keep a CHANGELOG](http://keepachangelog.com/) princip
## NEXT - YYYY-MM-DD

### Added
- Support for `halaxa/json-machine: ^0.8|^1.0`
- Nothing

### Deprecated
- Nothing
Expand All @@ -16,7 +16,7 @@ Updates should follow the [Keep a CHANGELOG](http://keepachangelog.com/) princip
- Nothing

### Removed
- Support for `halaxa/json-machine: ^0.7`
- Nothing

### Security
- Nothing
Expand Down
180 changes: 108 additions & 72 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,147 +2,183 @@

[![Author][ico-author]][link-author]
[![PHP Version][ico-php]][link-php]
[![Laravel Version][ico-laravel]][link-laravel]
[![Octane Compatibility][ico-octane]][link-octane]
[![Build Status][ico-actions]][link-actions]
[![Coverage Status][ico-scrutinizer]][link-scrutinizer]
[![Quality Score][ico-code-quality]][link-code-quality]
[![PHPStan Level][ico-phpstan]][link-phpstan]
[![Latest Version][ico-version]][link-packagist]
[![Software License][ico-license]](LICENSE.md)
[![PSR-7][ico-psr7]][link-psr7]
[![PSR-12][ico-psr12]][link-psr12]
[![PER][ico-per]][link-per]
[![Total Downloads][ico-downloads]][link-downloads]

Framework agnostic package to load heavy JSON in [lazy collections](https://laravel.com/docs/collections#lazy-collections). Under the hood, the brilliant [JSON Machine](https://github.com/halaxa/json-machine) by [@halaxa](https://github.com/halaxa) is used as a lexer and parser.
```php
LazyCollection::fromJson($source, 'data.*.users.*')
->map($this->mapToUser(...))
->filter($this->filterUser(...))
->values()
->chunk(1_000)
->each($this->storeUsersChunk(...));
```

Framework-agnostic package to load JSON of any size and from any source into [Laravel lazy collections](https://laravel.com/docs/collections#lazy-collections).

Lazy JSON recursively turns any JSON array or object into a lazy collection, consuming only a few KB of memory while parsing JSON of any dimension.

Need to load paginated items of JSON APIs? Consider using [Lazy JSON Pages](https://github.com/cerbero90/lazy-json-pages) instead.
It optionally allows to extract only some sub-trees, instead of the whole JSON, with an easy dot-notation syntax.

Under the hood, [🧩 JSON Parser](https://github.com/cerbero90/json-parser) is used to parse JSONs and extract sub-trees.

## Install
Need to lazy load items from paginated JSON APIs? Consider using [🐼 Lazy JSON Pages](https://github.com/cerbero90/lazy-json-pages) instead.

In a Laravel application, all you need to do is requiring the package:

## 📦 Install

Via Composer:

``` bash
composer require cerbero/lazy-json
```

Otherwise, you also need to register the lazy collection macro manually:
## 🔮 Usage

```php
use Cerbero\LazyJson\Macro;
use Illuminate\Support\LazyCollection;
* [👣 Basics](#-basics)
* [💧 Sources](#-sources)
* [🎯 Dots](#-dots)

LazyCollection::macro('fromJson', new Macro());
```

## Usage
### 👣 Basics

Loading JSON in lazy collections is possible by using the collection itself or the included helper:
Depending on our coding style, we can call Lazy JSON in 3 different ways:

```php
LazyCollection::fromJson($source);
use Cerbero\LazyJson\LazyJson;
use Illuminate\Support\LazyCollection;

use function Cerbero\LazyJson\lazyJson;

// auto-registered lazy collection macro
$lazyCollection = LazyCollection::fromJson($source);

// static method
$lazyCollection = LazyJson::from($source);

lazyJson($source);
// namespaced helper
$lazyCollection = lazyJson($source);
```

The following are the supported JSON sources:
The variable `$source` in our examples represents any [JSON source](#-sources). Once we define the source, we can chain any method of the [Laravel lazy collection](https://laravel.com/docs/collections#lazy-collections) to process the JSON in a memory-efficient way:

```php
$source = '{"foo":"bar"}'; // JSON string
$source = ['{"foo":"bar"}']; // any iterable containing JSON, i.e. array or Traversable
$source = 'https://foo.test/endpoint'; // endpoint
$source = Http::get('https://foo.test/endpoint'); // Laravel HTTP client response
$source = '/path/to/file.json'; // JSON file
$source = fopen('/path/to/file.json', 'rb'); // any resource
$source = <Psr\Http\Message\MessageInterface>; // any PSR-7 message, e.g. Guzzle response
$source = <Psr\Http\Message\StreamInterface>; // any PSR-7 stream
LazyCollection::fromJson($source)
->values()
->map(/* ... */)
->where(/* ... */)
->each(/* ... */);
```

Optionally, you can define a dot-noted path to extract only a sub-tree of the JSON. For example, given the following JSON:

```json
{
"data": [
{
"name": "Team 1",
"users": [
{
"id": 1
},
{
"id": 2
}
]
},
{
"name": "Team 2",
"users": [
{
"id": 3
}
]
}
]
}

### 💧 Sources

A JSON source is any data point that provides a JSON. A wide range of sources are supported by default:
- **strings**, e.g. `{"foo":"bar"}`
- **iterables**, i.e. arrays or instances of `Traversable`
- **file paths**, e.g. `/path/to/large.json`
- **resources**, e.g. streams
- **API endpoint URLs**, e.g. `https://endpoint.json` or any instance of `Psr\Http\Message\UriInterface`
- **PSR-7 requests**, i.e. any instance of `Psr\Http\Message\RequestInterface`
- **PSR-7 messages**, i.e. any instance of `Psr\Http\Message\MessageInterface`
- **PSR-7 streams**, i.e. any instance of `Psr\Http\Message\StreamInterface`
- **Laravel HTTP client requests**, i.e. any instance of `Illuminate\Http\Client\Request`
- **Laravel HTTP client responses**, i.e. any instance of `Illuminate\Http\Client\Response`
- **user-defined sources**, i.e. any instance of `Cerbero\JsonParser\Sources\Source`

For more information about JSON sources, please consult the [🧩 JSON Parser documentation](https://github.com/cerbero90/json-parser).


### 🎯 Dots

If we only need a sub-tree of a large JSON, we can use a simple dot-notation syntax to extract the desired path (or **dot**).

Consider [this JSON](https://randomuser.me/api/1.4?seed=json-parser&results=5) for example. To extract only the cities and avoid parsing the rest of the JSON, we can set the `results.*.location.city` dot:

```php
$source = 'https://randomuser.me/api/1.4?seed=json-parser&results=5';

$dot = 'results.*.location.city';

LazyCollection::fromJson($source, $dot)->each(function (string $value, string $key) {
// 1st iteration: $key === 'city', $value === 'Sontra'
// 2nd iteration: $key === 'city', $value === 'San Rafael Tlanalapan'
// 3rd iteration: $key === 'city', $value === 'گرگان'
// ...
});
```

defining the path `data.*.users.*.id` would iterate only user IDs:
The dot-notation syntax is very simple and it can include any of the following 4 elements:
- a key of a JSON array, e.g. `0`
- a key of a JSON object, e.g. `results`
- a dot to indicate the nesting level within a JSON, e.g. `results.0`
- an asterisk to indicate all items within an array, e.g. `results.*`

If we need to extract several sub-trees, Lazy JSON supports multiple dots:

```php
$ids = lazyJson($source, 'data.*.users.*.id')
->filter(fn ($id) => $id % 2 == 0)
->all();
$dots = ['results.*.gender', 'results.*.email'];

LazyCollection::fromJson($source, $dots)->each(function (string $value, string $key) {
// 1st iteration: $key === 'gender', $value === 'female'
// 2nd iteration: $key === 'email', $value === 'sara.meder@example.com'
// 3rd iteration: $key === 'gender', $value === 'female'
// 4th iteration: $key === 'email', $value === 'andrea.roque@example.com'
// ...
});
```

## Change log
## 📆 Change log

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

## Testing
## 🧪 Testing

``` bash
composer test
```

## Contributing
## 💞 Contributing

Please see [CONTRIBUTING](CONTRIBUTING.md) and [CODE_OF_CONDUCT](CODE_OF_CONDUCT.md) for details.

## Security
## 🧯 Security

If you discover any security related issues, please email andrea.marco.sartori@gmail.com instead of using the issue tracker.

## Credits
## 🏅 Credits

- [Andrea Marco Sartori][link-author]
- [All Contributors][link-contributors]

## License
## ⚖️ License

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

[ico-author]: https://img.shields.io/static/v1?label=author&message=cerbero90&color=50ABF1&logo=twitter&style=flat-square
[ico-php]: https://img.shields.io/packagist/php-v/cerbero/lazy-json?color=%234F5B93&logo=php&style=flat-square
[ico-laravel]: https://img.shields.io/static/v1?label=laravel&message=%E2%89%A56.0&color=ff2d20&logo=laravel&style=flat-square
[ico-octane]: https://img.shields.io/static/v1?label=octane&message=compatible&color=ff2d20&logo=laravel&style=flat-square
[ico-version]: https://img.shields.io/packagist/v/cerbero/lazy-json.svg?label=version&style=flat-square
[ico-actions]: https://img.shields.io/github/workflow/status/cerbero90/lazy-json/build?style=flat-square&logo=github
[ico-actions]: https://img.shields.io/github/actions/workflow/status/cerbero90/json-parser/build.yml?branch=master&style=flat-square&logo=github
[ico-license]: https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square
[ico-psr7]: https://img.shields.io/static/v1?label=compliance&message=PSR-7&color=blue&style=flat-square
[ico-psr12]: https://img.shields.io/static/v1?label=compliance&message=PSR-12&color=blue&style=flat-square
[ico-per]: https://img.shields.io/static/v1?label=compliance&message=PER&color=blue&style=flat-square
[ico-scrutinizer]: https://img.shields.io/scrutinizer/coverage/g/cerbero90/lazy-json.svg?style=flat-square&logo=scrutinizer
[ico-code-quality]: https://img.shields.io/scrutinizer/g/cerbero90/lazy-json.svg?style=flat-square&logo=scrutinizer
[ico-phpstan]: https://img.shields.io/badge/level-max-success?style=flat-square&logo=data:image/png;base64,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
[ico-downloads]: https://img.shields.io/packagist/dt/cerbero/lazy-json.svg?style=flat-square

[link-author]: https://twitter.com/cerbero90
[link-php]: https://www.php.net
[link-laravel]: https://laravel.com
[link-octane]: https://github.com/laravel/octane
[link-packagist]: https://packagist.org/packages/cerbero/lazy-json
[link-actions]: https://github.com/cerbero90/lazy-json/actions?query=workflow%3Abuild
[link-psr7]: https://www.php-fig.org/psr/psr-7/
[link-psr12]: https://www.php-fig.org/psr/psr-12/
[link-per]: https://www.php-fig.org/per/coding-style/
[link-scrutinizer]: https://scrutinizer-ci.com/g/cerbero90/lazy-json/code-structure
[link-code-quality]: https://scrutinizer-ci.com/g/cerbero90/lazy-json
[link-downloads]: https://packagist.org/packages/cerbero/lazy-json
[link-phpstan]: https://phpstan.org/
[link-contributors]: ../../contributors
Loading

0 comments on commit 63d402a

Please sign in to comment.