Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: CI

on:
push:
branches:
- "**"
pull_request:

jobs:
test:
runs-on: ubuntu-latest
name: PHP ${{ matrix.php }} - ${{ matrix.dependencies }}

strategy:
fail-fast: false
matrix:
include:
- php: "8.3"
dependencies: "prefer-lowest"
composer_flags: "--prefer-lowest"
- php: "8.4"
dependencies: "prefer-stable"
composer_flags: "--prefer-stable"

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
coverage: none
tools: composer:v2

- name: Validate Composer files
run: composer validate --strict

- name: Install dependencies
run: composer update --no-interaction --prefer-dist ${{ matrix.composer_flags }}

- name: Run tests
run: vendor/bin/phpunit tests

50 changes: 50 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

---

## [2.0.0] - 2026-03-26

### Breaking Changes
- **PHP requirement raised** to `^8.2` (dropped support for PHP < 8.2).
- **Laravel requirement raised** to `^13.0` (dropped support for Laravel 4.x–9.x).
- `setAttribute()` now returns `$this` (fluent) instead of `void`. Code that assigned the result or checked for void is unaffected.
- `setJsonAttribute()` now returns `$this` (fluent) instead of `void`. Same impact as above.
- `mutateAttribute()` now returns explicit `null` for missing or JSON-operator keys instead of a bare `return;`. Any code asserting strict `void` returns must be updated.

### Added
- GitHub Actions CI matrix covering PHP `8.2`, `8.3`, and `8.4` against Laravel 13 (`--prefer-lowest` and `--prefer-stable` variants).
- `orchestra/testbench ^11.0` added as a dev dependency for package-level integration test support.
- `getJsonOperatorPattern()` private helper on the `Json` trait — centralises the regex used to detect PSQL JSON operators (`->`, `->>`, `#>`, `#>>`).
- `extractJsonAttributeKey()` private helper on the `Json` trait — extracts the terminal key from a JSON expression such as `column->>'key'`.
- Regression test suite (`tests/RegressionTest.php`) with 15 tests covering JSON attribute discovery, visibility, get/set, hinting, dirty tracking, operator handling, and error cases.

### Changed
- Replaced removed `array_get()` global helper with `data_get()` in `getDirty()` (`illuminate/support` ^9 removed the global helper).
- `getMutatedAttributes()` now deduplicates the merged attribute list — duplicate keys from parent mutators and JSON columns will no longer appear more than once.
- `hasGetMutator()` refactored to use centralised `getJsonOperatorPattern()`.
- `mutateAttribute()` key extraction refactored to use `extractJsonAttributeKey()`.
- `phpunit.xml.dist` migrated to the PHPUnit 11.5 schema.
- `tests/JsonDialectTest.php` migrated from `PHPUnit_Framework_TestCase` to `PHPUnit\Framework\TestCase`.
- All `@expectedException` annotations replaced with `$this->expectException(...)` method calls.
- `tests/src/MockJsonDialectModel.php` updated to use `Illuminate\Database\Eloquent\Model` directly (removed legacy boot-array hack incompatible with modern Eloquent).

### Removed
- Travis CI configuration (`.travis.yml` retained for historical reference but is no longer active — replaced by GitHub Actions).
- Dev dependency on `phpunit/phpunit: 4.*`.
- Support for the legacy `Eloquent` model alias — models must now extend `Illuminate\Database\Eloquent\Model` directly.

### Security
- No security advisories found in updated dependencies (`composer audit` clean).

---

## [1.x] - Legacy

Supported Laravel 4.x through 9.x and PHP >= 5.4.
See Git history for individual change details.

73 changes: 63 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,41 @@
# Dialect

[![Build Status](https://travis-ci.org/darrylkuhn/dialect.svg?branch=master)](https://travis-ci.org/darrylkuhn/dialect) [![Code Climate](https://codeclimate.com/github/darrylkuhn/dialect/badges/gpa.svg)](https://codeclimate.com/github/darrylkuhn/dialect) [![Test Coverage](https://codeclimate.com/github/darrylkuhn/dialect/badges/coverage.svg)](https://codeclimate.com/github/darrylkuhn/dialect)
[![CI](https://github.com/aristamd/dialect/actions/workflows/ci.yml/badge.svg)](https://github.com/aristamd/dialect/actions/workflows/ci.yml)

Dialect provides JSON datatype support for the [Eloquent ORM](http://laravel.com/docs/eloquent). At this point this implementation is pretty bare bones and has been demonstrated to work with PostgreSQL and MySQL. There are lots of opportunities to enhance and improve. If you're interested in contributing please submit merge/pull requests.
Dialect provides JSON datatype support for the [Eloquent ORM](http://laravel.com/docs/eloquent). It has been demonstrated to work with PostgreSQL and MySQL. There are lots of opportunities to enhance and improve — if you're interested in contributing please submit pull requests.

## Requirements

| Dialect | Laravel | PHP |
|---------|---------|--------|
| 2.x | ^13.0 | ^8.2 |
| 1.x | 4.x – 9.x | >=5.4 |

## Installation

Require this package in your `composer.json` file:
Require this package via Composer:

```bash
composer require aristamd/dialect
```

Or add it manually to your `composer.json`:

`"darrylkuhn/dialect": "dev-master"`
```json
"aristamd/dialect": "^2.0"
```

...then run `composer update` to download the package to your vendor directory.
...then run `composer update`.

## Usage
### The Basics

The feature is exposed through a trait called which allows you to define attributes on the model which are of the json datatype. When the model is read in it will parse the JSON document and set up getters and setters for each top level attribute making it easy to interact with the various attributes within the document. For example we could create a Photos model like this:
The feature is exposed through a trait which allows you to define attributes on the model that use the JSON column type. When a model is hydrated it will parse the JSON document and set up getters and setters for each top-level attribute, making it easy to interact with the underlying data. For example, we could create a Photos model like this:

```php
class Photo extends Eloquent
use Illuminate\Database\Eloquent\Model;

class Photo extends Model
{
use Eloquent\Dialect\Json;
protected $jsonColumns = ['json_data'];
Expand Down Expand Up @@ -48,7 +65,9 @@ public function user()
### Structure Hinting
Sometimes you may have an empty or partially populated record in which case the trait cannot automatically detect and create getters/setters, etc... When getting or setting an attribute not previously set in the JSON document you'll get an exception. You have two choices to deal with this. You can hint at the full structure as in the example below:
```php
class Photo extends Eloquent
use Illuminate\Database\Eloquent\Model;

class Photo extends Model
{
use Eloquent\Dialect\Json;
protected $jsonColumns = ['json_data'];
Expand All @@ -67,7 +86,9 @@ $photo->setJsonAttribute( 'json_data', 'fizz', 'buzz' );
### Showing/Hiding Attributes
One of the aims of the project is to make json attributes "first class" citizens of the model. This means by default we add the attributes to the models appends array so that when you call `$model->toArray()` or `$model->toJson()` the attribute shows up as a part of the structure like a normal attribute. By default we also hide away the json column holding the underlying data. Both of these settings can be changed using the `showJsonColumns()` and `showJsonAttributes()` as shown below:
```php
class Photo extends Eloquent
use Illuminate\Database\Eloquent\Model;

class Photo extends Model
{
use Eloquent\Dialect\Json;
protected $jsonColumns = ['json_data'];
Expand All @@ -79,4 +100,36 @@ class Photo extends Eloquent
$this->showJsonAttributes(false);
}
}
```
```

## Upgrading from 1.x to 2.x

2.x is a breaking release that drops legacy framework support in favour of Laravel 13 and PHP 8.2+.

### Required changes

| Before (1.x) | After (2.x) |
|---|---|
| `class Photo extends Eloquent` | `class Photo extends Illuminate\Database\Eloquent\Model` |
| `illuminate/support: 4.*–9.*` | `illuminate/support: ^13.0` |
| `php: >=5.4` | `php: ^8.2` |
| PHPUnit 4.x in tests | PHPUnit 11.x |

### Behavioral changes
- `setAttribute()` and `setJsonAttribute()` now return `$this` (fluent) instead of `void`, consistent with Laravel 13 Eloquent contracts. Call chains that previously ignored the return value are unaffected.
- `mutateAttribute()` now returns explicit `null` instead of a bare `return;` for missing/JSON-operator keys.
- `getMutatedAttributes()` deduplicates merged attribute lists, so duplicate keys from parent and JSON columns will no longer appear twice.
- The global `array_get()` helper (removed in Laravel 9) has been replaced with `data_get()` internally. No public API change.

## Contributing

Pull requests are welcome. Please ensure all tests pass before submitting:

```bash
composer install
vendor/bin/phpunit tests
```

## License

This package is open-sourced software licensed under the [MIT license](LICENSE).
9 changes: 5 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@
}
],
"require": {
"php": ">=5.4.0",
"illuminate/support": "4.*|5.*|6.*|7.*|8.*|9.*"
"php": "^8.3",
"illuminate/support": "^13.0"
},
"require-dev": {
"phpunit/phpunit": "4.*",
"illuminate/database": "4.*|5.*|6.*|7.*|8.*|9.*"
"phpunit/phpunit": "^11.5",
"orchestra/testbench": "^11.0",
"illuminate/database": "^13.0"
},
"autoload": {
"psr-4": {
Expand Down
Loading
Loading