Full Code of darrylkuhn/dialect for AI

master 8a53af697c5a cached
13 files
28.7 KB
7.3k tokens
34 symbols
1 requests
Download .txt
Repository: darrylkuhn/dialect
Branch: master
Commit: 8a53af697c5a
Files: 13
Total size: 28.7 KB

Directory structure:
gitextract_zy_74zq0/

├── .gitignore
├── .travis.yml
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── composer.json
├── configureComposer.sh
├── phpunit.xml.dist
├── src/
│   └── Dialect/
│       ├── InvalidJsonException.php
│       └── Json.php
└── tests/
    ├── JsonDialectTest.php
    ├── bootstrap.php
    └── src/
        └── MockJsonDialectModel.php

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
/bootstrap/compiled.php
/vendor
/build

================================================
FILE: .travis.yml
================================================
before_script: 
  - ./configureComposer.sh
  - composer self-update
  - composer install --prefer-dist --no-interaction
php: 
  - 5.5
  - 5.6
  - 7.0
  - hhvm
language: php
after_script: CODECLIMATE_REPO_TOKEN=a8b5ee59cd6658ea5710614ec4285cdc81084aeee33f531bd30d1e317c88257c ./vendor/bin/test-reporter
script: ./vendor/bin/phpunit --coverage-clover build/logs/clover.xml
env: 
  global: 
    secure: uuQ9h1nIwseYMpOBV+j+kOqgD/mPkfMk411b/jxvM0c0u4b9E2q7aep238IDUxqDLFBV4QwGWJobgSBdu/bpm0GQC29CYHhgw+DVceCZ6VlcStTwVvX+XfyBBOVn/KTbEZQapC1lsGneBEzKzp9esUqYW3uOjJPeA9ah20zjV2c=
matrix: 
  allow_failures: 
  - php: hhvm


================================================
FILE: CONTRIBUTING.md
================================================
Contributing
============

First off - **you rock!**, thanks you so much for taking to time and energy to make this project better!

A healthy community is filled with different coders with differnt coding styles. This can cause cognative friction. 
Here are a few rules to follow in order to minimize that friction and ease code reviews and discussions before 
maintainers accept and merge your work.

You MUST follow the [PSR-1](http://www.php-fig.org/psr/1/) and [PSR-2](http://www.php-fig.org/psr/2/). Use
[PHP-CS-Fixer](http://cs.sensiolabs.org/) to make this task easier if you are unfamiliar with these stantards.
Additionally you:
# MUST run the test suite.
# MUST write (or update) unit tests.
# SHOULD write documentation.

Please, write [commit messages that make sense](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html),
and [rebase your branch](http://git-scm.com/book/en/Git-Branching-Rebasing) before submitting your Pull Request.

You may be asked to [squash your commits](http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html)
too. This is used to "clean" your Pull Request before merging it (we don't want commits such as `fix tests`, `fix 2`,
`fix 3`, etc).

Let's do this!

================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2015 Darryl Kuhn

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.



================================================
FILE: README.md
================================================
# 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)

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.

## Installation

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

`"darrylkuhn/dialect": "dev-master"`

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

## 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:

```php
class Photo extends Eloquent
{
    use Eloquent\Dialect\Json;
    protected $jsonColumns = ['json_data'];
}
```
And then this:
```php
$attr = json_decode($photo->json_data);
$attr->key = $value;
$photo->json_data = json_encode($attr);
```
becomes this:
```php
$photo->key = value;
```
Also when calling the toArray() method the attributes are moved to the top level and the 'json_attributes' column is hidden. This essentially hides away the fact that you're using the json datatype and makes it look like we're working with attributes directly.

### Relations
You can also establish relationships on a model like this (only supported in PostgreSQL):
```php
public function user()
{
    return $this->hasOne( 'User', 'id', "json_data->>'user_id'" );
}
```

### 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 Eloquent\Dialect\Json;
    protected $jsonColumns = ['json_data'];

    public function __construct()
    {
        parent::__construct();
        $this->hintJsonStructure( 'json_data', '{"foo":null}' );
    }
}
```
Once you create a hint you will be able to make calls to get and set json attributes e.g. `$photo->foo = 'bar';` regardless of whether or not they are already defined in the underlying db record. Alternatly if you prefer not to hint structures then you may call `setJsonAttribute()`. For example if you defined a json column called "json_data" and wanted to set an attribute called 'fizz' so you could call:
```php
$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 Eloquent\Dialect\Json;
    protected $jsonColumns = ['json_data'];

    public function __construct()
    {
        parent::__construct();
        $this->showJsonColumns(true);
        $this->showJsonAttributes(false);
    }
}
```

================================================
FILE: composer.json
================================================
{
    "name": "darrylkuhn/dialect",
    "description": "Provides JSON datatype support for the Eloquent ORM",
    "keywords": ["laravel","illuminate","eloquent","json","schemaless","schema-free"],
    "license": "MIT",
    "authors": [
        {
            "name": "Darryl Kuhn",
            "email": "darryl.kuhn@gmail.com"
        }
    ],
    "require": {
        "php": ">=5.4.0",
        "illuminate/support": "4.*|5.*|6.*|7.*"
    },
    "require-dev": {
        "phpunit/phpunit": "4.*",
        "illuminate/database": "4.*|5.*|6.*|7.*",
        "codeclimate/php-test-reporter": "dev-master"
    },
    "autoload": {
        "psr-4": {
            "Eloquent\\Dialect\\": "src/Dialect/"
        }
    }
}

================================================
FILE: configureComposer.sh
================================================
if [ "$TRAVIS_SECURE_ENV_VARS" = "true" ];
then
mkdir ~/.composer/
echo '{ "config": {"github-oauth":{"github.com": ' > ~/.composer/config.json
echo "\"$GH_OAUTH\"" >> ~/.composer/config.json
echo '}}}' >> ~/.composer/config.json
fi

================================================
FILE: phpunit.xml.dist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
         backupStaticAttributes="false"
         bootstrap="tests/bootstrap.php"
         colors="true"
         convertErrorsToExceptions="true"
         convertNoticesToExceptions="true"
         convertWarningsToExceptions="true"
         processIsolation="false"
         stopOnFailure="false"
         syntaxCheck="false"
>
    <testsuites>
        <testsuite name="Dialect Test Suite">
            <directory suffix=".php">./tests/</directory>
        </testsuite>
    </testsuites>
  <filter>
    <whitelist>
      <directory suffix=".php">.</directory>
      <exclude>
        <directory suffix=".php">tests</directory>
        <directory suffix=".php">vendor</directory>
      </exclude>
    </whitelist>
  </filter>
</phpunit>

================================================
FILE: src/Dialect/InvalidJsonException.php
================================================
<?php namespace Eloquent\Dialect;

class InvalidJsonException extends \Exception
{
    
}

================================================
FILE: src/Dialect/Json.php
================================================
<?php

namespace Eloquent\Dialect;

trait Json
{
    /**
     * List of known PSQL JSON operators. This is used when determining if
     * a column reference matches that of a JSON pattern (e.g.
     * test_column->>'value').
     *
     * @var array
     */
    public static $jsonOperators = [
        '->',
        '->>',
        '#>',
        '#>>', ];

    /**
     * Holds the map of attributes and the JSON colums they are stored in. This
     * will take the form of:
     *  [ 'json_element_1' => 'original_column',
     *    'json_element_2' => 'original_column' ].
     *
     * @var array
     */
    private $jsonAttributes = [];

    /**
     * Holds a list of column names and the structure they *may* contain (e.g.
     * ['json_column' => "{'foo':null}"].
     *
     * @var array
     */
    private $hintedJsonAttributes = [];

    /**
     * By default this trait will hide the json columns when rendering the
     * model using toArray() or toJson() only exposing the underlying JSON
     * parameters as top level paremters on the model. Set this parameter to
     * true if you want to change that behavior.
     *
     * @var bool
     */
    private $showJsonColumns = false;

    /**
     * By default this trait will append the json attributes when rendering the
     * model using toArray() or toJson(). Set this parameter to false if you
     * want to change that behavior.
     *
     * @var bool
     */
    private $showJsonAttributes = true;

    /**
     * Create a new model instance that is existing.
     * Overrides parent to set Json columns.
     *
     * @param array       $attributes
     * @param string|null $connection
     *
     * @return static
     */
    public function newFromBuilder($attributes = array(), $connection = null)
    {
        $model = parent::newFromBuilder($attributes, $connection);
        $model->inspectJsonColumns();
        $model->addHintedAttributes();

        return $model;
    }

    /**
     * Decodes each of the declared JSON attributes and records the attributes
     * on each.
     */
    public function inspectJsonColumns()
    {
        foreach ($this->jsonColumns as $col) {
            if (!$this->showJsonColumns) {
                $this->hidden[] = $col;
            }

            if(array_key_exists($col, $this->attributes)) {
                $obj = json_decode($this->attributes[$col]);
            }
            else {
                $obj = json_decode($this->$col);
            }

            if (is_object($obj)) {
                foreach ($obj as $key => $value) {
                    $this->flagJsonAttribute($key, $col);
                    if ($this->showJsonAttributes) {
                        $this->appends[] = $key;
                    }
                }
            }
        }
    }

    /**
     * Schema free data architecture give us tons of flexibility (yay) but
     * makes it hard to inspect a structure and build getters/setters.
     * Therefore you can "hint" the structure to make life easier.
     */
    public function addHintedAttributes()
    {
        foreach ($this->hintedJsonAttributes as $col => $structure) {
            if (!$this->showJsonColumns) {
                $this->hidden[] = $col;
            }

            if (json_decode($structure) === null) {
                throw new InvalidJsonException();
            }

            $obj = json_decode($structure);

            if (is_object($obj)) {
                foreach ($obj as $key => $value) {
                    $this->flagJsonAttribute($key, $col);
                    if ($this->showJsonAttributes) {
                        $this->appends[] = $key;
                    }
                }
            }
        }
    }

    /**
     * Sets a hint for a given column.
     *
     * @param string $column    name of column that we're hinting
     * @param string $structure json encoded structure
     *
     * @throws InvalidJsonException
     */
    public function hintJsonStructure($column, $structure)
    {
        if (json_decode($structure) === null) {
            throw new InvalidJsonException();
        }

        $this->hintedJsonAttributes[$column] = $structure;

        // Run the call to add hinted attributes to the internal json
        // attributes array. This allows callers to get/set parameters when
        // working with new models
        $this->addHintedAttributes();
    }

    /**
     * Record that a given JSON element is found on a particular column.
     *
     * @param string $key attribute name within the JSON column
     * @param string $col name of JSON column
     */
    public function flagJsonAttribute($key, $col)
    {
        $this->jsonAttributes[$key] = $col;
    }

    /**
     * Include JSON column in the list of attributes that have a get mutator.
     *
     * @param string $key
     *
     * @return bool
     */
    public function hasGetMutator($key)
    {
        $jsonPattern = '/'.implode('|', self::$jsonOperators).'/';

        if (array_key_exists($key, $this->jsonAttributes) !== false) {
            return true;
        } // In some cases the key specified may not be a simple key but rather a
        // JSON expression (e.g. "jsonField->'some_key'). A common case would
        // be when specifying a relation key. As such we test for JSON
        // operators and expect a mutator if this is a JSON expression
        elseif (preg_match($jsonPattern, $key) != false) {
            return true;
        }

        return parent::hasGetMutator($key);
    }

    /**
     * Include the JSON attributes in the list of mutated attributes for a
     * given instance.
     *
     * @return array
     */
    public function getMutatedAttributes()
    {
        $attributes = parent::getMutatedAttributes();
        $jsonAttributes = array_keys($this->jsonAttributes);

        return array_merge($attributes, $jsonAttributes);
    }

    /**
     * Check if the key is a known json attribute and return that value.
     *
     * @param string $key
     * @param mixed  $value
     *
     * @return mixed
     *
     * @throws InvalidJsonException
     */
    protected function mutateAttribute($key, $value)
    {
        $jsonPattern = '/'.implode('|', self::$jsonOperators).'/';

        // Test for JSON operators and reduce to end element
        $containsJsonOperator = false;

        if (preg_match($jsonPattern, $key)) {
            $elems = preg_split($jsonPattern, $key);
            $key = end($elems);
            $key = str_replace(['>', "'"], '', $key);

            $containsJsonOperator = true;
        }

        if (!parent::hasGetMutator($key) && array_key_exists($key, $this->jsonAttributes) != false) {

            // Get the content of the column associated with this JSON
            // attribute and parse it into an object
            $value = $this->{$this->jsonAttributes[$key]};
            $obj = json_decode($this->{$this->jsonAttributes[$key]});

            // Make sure we were able to parse the json. It's possible here
            // that we've only hinted at an attribute and the column that will
            // hold that attribute is actually null. This isn't really a parse
            // error though the json_encode method will return null (just like)
            // a parse error. To distinguish the two states see if the original
            // value was null (indicating there was nothing there to parse in
            // the first place)
            if ( !($value === 'null' || $value === null) && $obj === null ) {
                throw new InvalidJsonException();
            }

            // Again it's possible the key will be in the jsonAttributes array
            // (having been hinted) but not present on the actual record.
            // Therefore test that the key is set before returning.
            if (isset($obj->$key)) {
                return $obj->$key;
            } else {
                return;
            }
        } elseif ($containsJsonOperator) {
            return;
        }

        return parent::mutateAttribute($key, $value);
    }

    /**
     * Set a given attribute on the known JSON elements.
     *
     * @param string $key
     * @param mixed  $value
     */
    public function setAttribute($key, $value)
    {
        if (array_key_exists($key, $this->jsonAttributes) !== false && !parent::hasSetMutator($key)) {
            $this->setJsonAttribute($this->jsonAttributes[$key], $key, $value);

            return;
        }

        parent::setAttribute($key, $value);
    }

    /**
     * Set a given attribute on the known JSON elements.
     *
     * @param string $attribute
     * @param string $key
     * @param mixed  $value
     */
    public function setJsonAttribute($attribute, $key, $value)
    {
        // Pull the attribute and decode it
        $decoded = json_decode($this->{$attribute});

        switch (gettype($decoded)) {
            // It's possible the attribute doesn't exist yet (since we can hint at
            // structure). In that case we build an object to set values on as a
            // starting point
            case 'NULL':
                $decoded = json_decode('{}');
                $decoded->$key = $value;
                break;

            case 'array':
                $decoded[$key] = $value;
                break;

            default:
                $decoded->$key = $value;
                break;
        }

        $this->flagJsonAttribute($key, $attribute);
        $this->{$attribute} = json_encode($decoded);

        return;
    }

    /**
     * Add json attributes to the list of things that have changed (when
     * they've changed).
     *
     * @return array
     */
    public function getDirty($includeJson = false)
    {
        $dirty = parent::getDirty();

        if (!$includeJson) {
            return $dirty;
        }

        foreach (array_unique($this->jsonAttributes) as $attribute) {
            $originals[$attribute] = json_decode(array_get($this->original, $attribute, 'null'), true);
        }

        foreach ($this->jsonAttributes as $jsonAttribute => $jsonColumn) {
            if ($this->$jsonAttribute !== null &&
                $this->$jsonAttribute !== array_get($originals[$jsonColumn], $jsonAttribute)) {
                $dirty[$jsonAttribute] = json_encode($this->$jsonAttribute);
            }
        }

        return $dirty;
    }

    /**
     * Allows you to specify if the actual JSON column housing the attributes
     * should be shown on toArray() and toJson() calls. Set this value in the
     * models constructor (to make sure it is set before newFromBuilder() is
     * called). This is false by default.
     *
     * @param bool $show
     *
     * @return bool
     */
    public function showJsonColumns($show)
    {
        return $this->showJsonColumns = $show;
    }

    /**
     * Allows you to specify if the attributes within various json columns
     * should be shown on toArray() and toJson() calls. Set this value in the
     * models constructor (to make sure it is set before newFromBuilder() is
     * called). This is true by default.
     *
     * @param  bool show
     *
     * @return bool
     */
    public function showJsonAttributes($show)
    {
        return $this->showJsonAttributes = $show;
    }
}


================================================
FILE: tests/JsonDialectTest.php
================================================
<?php

class JsonDialectTest extends PHPUnit_Framework_TestCase
{
    /**
     * Assert that defined JSON attributes are properly parsed and exposed through
     * mutators.
     */
    public function testInspectJsonColumns()
    {
        // Mock the model with data
        $mock = new MockJsonDialectModel;
        $mock->setJsonColumns(['testColumn']);
        $mock->setAttribute('testColumn', json_encode(['foo' => 'bar']));

        // Execute the insepect call
        $mock->inspectJsonColumns();

        // Assert that the column were properly parsed and various bits have
        // been set on the model
        $this->assertTrue($mock->hasGetMutator('foo'));
        $this->assertContains('foo', $mock->getMutatedAttributes());
        $this->assertArrayNotHasKey('testColumn', $mock->toArray());
        $this->assertArrayHasKey('foo', $mock->toArray());
        $this->assertEquals($mock->foo, 'bar');
    }

    /**
     * Assert that the json columns show up when configured to do so
     */
    public function testDisableHiddenJsonColumns()
    {
        // Mock the model with data
        $mock = new MockJsonDialectModel;
        $mock->setJsonColumns(['testColumn']);
        $mock->setAttribute('testColumn', json_encode(['foo' => 'bar']));
        $mock->showJsonColumns(true);

        // Execute the insepect call
        $mock->inspectJsonColumns();

        // Assert that the testColumn shows up
        $this->assertArrayHasKey('testColumn', $mock->toArray());
    }

    /**
     * Assert that the json attributes do not show up when configured to do so
     */
    public function testEnableHiddenJsonAttributes()
    {
        // Mock the model with data
        $mock = new MockJsonDialectModel;
        $mock->setJsonColumns(['testColumn']);
        $mock->setAttribute('testColumn', json_encode(['foo' => 'bar']));
        $mock->showJsonAttributes(false);

        // Execute the insepect call
        $mock->inspectJsonColumns();

        // Assert that attribute isn't there
        $this->assertArrayNotHasKey('foo', $mock->toArray());
    }

    /**
     * Assert that an exception is thrown when given invalid json as a
     * structure hint
     *
     * @expectedException Eloquent\Dialect\InvalidJsonException
     */
    public function testInvalidJsonAttribute()
    {
        // Mock the model with data
        $mock = new MockJsonDialectModel;
        $mock->hintJsonStructure( 'testColumn', json_encode(['foo'=>null]) );

        // Set testColumn to invalid json
        $mock->setAttribute('testColumn', '{');

        // Try to access a property on invalid json - we should get an
        // exception
        $mock->foo;
    }

    /**
     * Assert that no exception is thrown when given null json
     */
    public function testNullJsonAttribute()
    {
        // Mock the model with data
        $mock = new MockJsonDialectModel;
        $mock->hintJsonStructure( 'testColumn', json_encode(['foo'=>null]) );

        // Set testColumn to 'null'
        $mock->setAttribute('testColumn', 'null');
        $this->assertNull($mock->foo);

        // Set testColumn to null
        $mock->setAttribute('testColumn', null);
        $this->assertNull($mock->foo);
    }

    /**
     * Assert that JSON attributes can be set through mutators
     */
    public function testSetAttribute()
    {
        // Mock the model with data
        $mock = new MockJsonDialectModel;
        $mock->setJsonColumns(['testColumn']);
        $mock->setAttribute('testColumn', json_encode(['foo' => 'bar']));

        // Execute the insepect call
        $mock->inspectJsonColumns();

        $mock->foo = 'baz';
        $mock->setJsonAttribute('testColumn', 'fizz', 'buzz');

        // Assert that the column were properly parsed and various bits have
        // been set on the model
        $this->assertEquals($mock->foo, 'baz');
        $this->assertEquals($mock->fizz, 'buzz');
    }

    /**
     * Assert that JSON array attributes can be set through mutators
     */
    public function testSetArrayAttribute()
    {
        // Mock the model with data
        $mock = new MockJsonDialectModel;
        $mock->hintJsonStructure( 'testColumn', json_encode(['foo'=>null]) );

        // Execute the hint call
        $mock->addHintedAttributes();

        $mock->foo = ['bar','baz'];

        // Assert that the column were properly parsed and various bits have
        // been set on the model
        $this->assertEquals('array', gettype($mock->foo));
        $this->assertEquals(2, count($mock->foo) );
        $this->assertEquals('bar', $mock->foo[0] );
    }

    /**
     * Assert that attributes with JSON operators are properly recognized as JSON
     * attributes
     */
    public function testGetMutator()
    {
        // Mock the model with data
        $mock = new MockJsonDialectModel;
        $this->assertTrue($mock->hasGetMutator("testColumn->>'foo'"));
    }

    /**
     * Assert that defined JSON attributes are properly parsed and exposed
     * through mutators.
     */
    public function testHintedJsonColumns()
    {
        // Mock the model with data
        $mock = new MockJsonDialectModel;
        $mock->hintJsonStructure( 'testColumn', json_encode(['foo'=>null]) );

        // Execute the hint call
        $mock->addHintedAttributes();

        // Assert that the column were properly parsed and various bits have
        // been set on the model
        $this->assertTrue($mock->hasGetMutator('foo'));
        $this->assertContains('foo', $mock->getMutatedAttributes());
        $this->assertContains('testColumn', $mock->getHidden());

        $this->assertNull( $mock->foo );

        // Set a value for foo
        $mock->foo = 'bar';

        // assert that the column has been set properly
        $this->assertEquals( 'bar', $mock->foo );
        $this->assertEquals( $mock->testColumn, json_encode(['foo'=>'bar']) );
    }

    /**
     * Assert that defined JSON attributes are returned in the getDirty()
     * response when expected.
     */
    public function testGetDirtyJson()
    {
        // Mock the model with data
        $mock = new MockJsonDialectModel;
        $mock->hintJsonStructure( 'testColumn', json_encode(['foo'=>null]) );

        // At this point 'foo' should not be dirty
        $this->assertArrayNotHasKey( 'foo', $mock->getDirty(true) );

        $mock->setAttribute('testColumn', json_encode(['foo' => 'bar']));

        // Now that 'foo' has been changed it should show up in the getDirty()
        // response
        $this->assertArrayHasKey( 'foo', $mock->getDirty(true) );
    }

    /**
     * Assert that an exception is thrown when given invalid json as a
     * structure hint
     *
     * @expectedException Eloquent\Dialect\InvalidJsonException
     */
    public function testInvalidHint()
    {
        // Mock the model with data
        $mock = new MockJsonDialectModel;
        $mock->hintJsonStructure( 'testColumn', '{' );

        // Execute the hint call
        $mock->addHintedAttributes();
    }

    /**
     * Test the ability to allow models to provide their own custom attribute
     * getters for json attributes
     */
    public function testCustomGetter()
    {
        // Mock the model with data
        $mock = new MockJsonDialectModel;
        $mock->hintJsonStructure( 'foo', json_encode(['custom_get'=>null]) );

        // Execute the hint call
        $mock->addHintedAttributes();

        // Assert that the column were properly parsed and various bits have
        // been set on the model
        $this->assertTrue($mock->hasGetMutator('custom_get'));
        $this->assertEquals($mock->custom_get, 'custom getter result');
    }

    /**
     * Test the ability to allow models to provide their own custom attribute
     * getters for json attributes
     */
    public function testCustomSetter()
    {
        // Mock the model with data
        $mock = new MockJsonDialectModel;
        $mock->hintJsonStructure( 'foo', json_encode(['custom_set'=>null]) );

        // Execute the hint call
        $mock->addHintedAttributes();

        // Assert that the column were properly parsed and various bits have
        // been set on the model
        $this->assertTrue($mock->hasSetMutator('custom_set'));

        // Set a value
        $mock->custom_set = 'value';

        // Assert that the attribute was mutated by the mutator on our mock
        // model
        $this->assertEquals($mock->custom_set, 'custom value');
    }
}

================================================
FILE: tests/bootstrap.php
================================================
<?php

require_once(__DIR__.'/../vendor/autoload.php');
require_once(dirname(__FILE__).'/src/MockJsonDialectModel.php');


================================================
FILE: tests/src/MockJsonDialectModel.php
================================================
<?php

class MockJsonDialectModel extends Illuminate\Database\Eloquent\Model
{
    use \Eloquent\Dialect\Json;

    protected $jsonColumns;

    public function __construct(array $attributes = array())
    {
        static::$booted[get_class($this)] = true;
        parent::__construct($attributes);
    }

    public function setJsonColumns(Array $columns)
    {
        $this->jsonColumns = $columns;
    }

    public function getCustomGetAttribute()
    {
        return "custom getter result";
    }

    public function setCustomSetAttribute( $value )
    {
        $this->setJsonAttribute($this->jsonAttributes['custom_set'], 'custom_set', "custom {$value}");
    }
}
Download .txt
gitextract_zy_74zq0/

├── .gitignore
├── .travis.yml
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── composer.json
├── configureComposer.sh
├── phpunit.xml.dist
├── src/
│   └── Dialect/
│       ├── InvalidJsonException.php
│       └── Json.php
└── tests/
    ├── JsonDialectTest.php
    ├── bootstrap.php
    └── src/
        └── MockJsonDialectModel.php
Download .txt
SYMBOL INDEX (34 symbols across 4 files)

FILE: src/Dialect/InvalidJsonException.php
  class InvalidJsonException (line 3) | class InvalidJsonException extends \Exception

FILE: src/Dialect/Json.php
  type Json (line 5) | trait Json
    method newFromBuilder (line 66) | public function newFromBuilder($attributes = array(), $connection = null)
    method inspectJsonColumns (line 79) | public function inspectJsonColumns()
    method addHintedAttributes (line 109) | public function addHintedAttributes()
    method hintJsonStructure (line 141) | public function hintJsonStructure($column, $structure)
    method flagJsonAttribute (line 161) | public function flagJsonAttribute($key, $col)
    method hasGetMutator (line 173) | public function hasGetMutator($key)
    method getMutatedAttributes (line 196) | public function getMutatedAttributes()
    method mutateAttribute (line 214) | protected function mutateAttribute($key, $value)
    method setAttribute (line 268) | public function setAttribute($key, $value)
    method setJsonAttribute (line 286) | public function setJsonAttribute($attribute, $key, $value)
    method getDirty (line 321) | public function getDirty($includeJson = false)
    method showJsonColumns (line 353) | public function showJsonColumns($show)
    method showJsonAttributes (line 368) | public function showJsonAttributes($show)

FILE: tests/JsonDialectTest.php
  class JsonDialectTest (line 3) | class JsonDialectTest extends PHPUnit_Framework_TestCase
    method testInspectJsonColumns (line 9) | public function testInspectJsonColumns()
    method testDisableHiddenJsonColumns (line 31) | public function testDisableHiddenJsonColumns()
    method testEnableHiddenJsonAttributes (line 49) | public function testEnableHiddenJsonAttributes()
    method testInvalidJsonAttribute (line 70) | public function testInvalidJsonAttribute()
    method testNullJsonAttribute (line 87) | public function testNullJsonAttribute()
    method testSetAttribute (line 105) | public function testSetAttribute()
    method testSetArrayAttribute (line 127) | public function testSetArrayAttribute()
    method testGetMutator (line 149) | public function testGetMutator()
    method testHintedJsonColumns (line 160) | public function testHintedJsonColumns()
    method testGetDirtyJson (line 189) | public function testGetDirtyJson()
    method testInvalidHint (line 211) | public function testInvalidHint()
    method testCustomGetter (line 225) | public function testCustomGetter()
    method testCustomSetter (line 244) | public function testCustomSetter()

FILE: tests/src/MockJsonDialectModel.php
  class MockJsonDialectModel (line 3) | class MockJsonDialectModel extends Illuminate\Database\Eloquent\Model
    method __construct (line 9) | public function __construct(array $attributes = array())
    method setJsonColumns (line 15) | public function setJsonColumns(Array $columns)
    method getCustomGetAttribute (line 20) | public function getCustomGetAttribute()
    method setCustomSetAttribute (line 25) | public function setCustomSetAttribute( $value )
Condensed preview — 13 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (31K chars).
[
  {
    "path": ".gitignore",
    "chars": 38,
    "preview": "/bootstrap/compiled.php\n/vendor\n/build"
  },
  {
    "path": ".travis.yml",
    "chars": 615,
    "preview": "before_script: \n  - ./configureComposer.sh\n  - composer self-update\n  - composer install --prefer-dist --no-interaction\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 1233,
    "preview": "Contributing\n============\n\nFirst off - **you rock!**, thanks you so much for taking to time and energy to make this proj"
  },
  {
    "path": "LICENSE",
    "chars": 1079,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2015 Darryl Kuhn\n\nPermission is hereby granted, free of charge, to any person obtai"
  },
  {
    "path": "README.md",
    "chars": 4013,
    "preview": "# Dialect\n\n[![Build Status](https://travis-ci.org/darrylkuhn/dialect.svg?branch=master)](https://travis-ci.org/darrylkuh"
  },
  {
    "path": "composer.json",
    "chars": 711,
    "preview": "{\n    \"name\": \"darrylkuhn/dialect\",\n    \"description\": \"Provides JSON datatype support for the Eloquent ORM\",\n    \"keywo"
  },
  {
    "path": "configureComposer.sh",
    "chars": 232,
    "preview": "if [ \"$TRAVIS_SECURE_ENV_VARS\" = \"true\" ];\nthen\nmkdir ~/.composer/\necho '{ \"config\": {\"github-oauth\":{\"github.com\": ' > "
  },
  {
    "path": "phpunit.xml.dist",
    "chars": 806,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit backupGlobals=\"false\"\n         backupStaticAttributes=\"false\"\n         b"
  },
  {
    "path": "src/Dialect/InvalidJsonException.php",
    "chars": 89,
    "preview": "<?php namespace Eloquent\\Dialect;\n\nclass InvalidJsonException extends \\Exception\n{\n    \n}"
  },
  {
    "path": "src/Dialect/Json.php",
    "chars": 11302,
    "preview": "<?php\n\nnamespace Eloquent\\Dialect;\n\ntrait Json\n{\n    /**\n     * List of known PSQL JSON operators. This is used when det"
  },
  {
    "path": "tests/JsonDialectTest.php",
    "chars": 8504,
    "preview": "<?php\n\nclass JsonDialectTest extends PHPUnit_Framework_TestCase\n{\n    /**\n     * Assert that defined JSON attributes are"
  },
  {
    "path": "tests/bootstrap.php",
    "chars": 121,
    "preview": "<?php\n\nrequire_once(__DIR__.'/../vendor/autoload.php');\nrequire_once(dirname(__FILE__).'/src/MockJsonDialectModel.php');"
  },
  {
    "path": "tests/src/MockJsonDialectModel.php",
    "chars": 675,
    "preview": "<?php\n\nclass MockJsonDialectModel extends Illuminate\\Database\\Eloquent\\Model\n{\n    use \\Eloquent\\Dialect\\Json;\n\n    prot"
  }
]

About this extraction

This page contains the full source code of the darrylkuhn/dialect GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 13 files (28.7 KB), approximately 7.3k tokens, and a symbol index with 34 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!