Skip to content
This repository has been archived by the owner on Feb 13, 2023. It is now read-only.

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
okdewit committed Mar 22, 2021
0 parents commit aaa48a8
Show file tree
Hide file tree
Showing 8 changed files with 251 additions and 0 deletions.
49 changes: 49 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: tests

on: [push]

jobs:
test:
name: PHP ${{ matrix.php }} - Laravel ${{ matrix.laravel }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
php: [8.0, 7.4, 7.1]
laravel: [8.*, 7.*, 6.*, 5.8.*]
os: [ubuntu-latest]
include:
- laravel: 8.*
testbench: 6.*
- laravel: 7.*
testbench: 5.*
- laravel: 6.*
testbench: 4.*
- laravel: 5.8.*
testbench: 3.8.*
exclude:
- laravel: 8.*
php: 7.1
- laravel: 7.*
php: 7.1
- laravel: 6.*
php: 7.1
- laravel: 5.8.*
php: 8.0
steps:
- name: Checkout code
uses: actions/checkout@v1

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

- name: Install dependencies
run: |
composer config -g github-oauth.github.com ${{ secrets.GITHUB_TOKEN }}
composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update
composer update --prefer-stable --prefer-dist --no-interaction --no-suggest
- name: Execute tests
run: composer test
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
composer.lock
.idea
vendor
21 changes: 21 additions & 0 deletions NullableObserver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php declare(strict_types=1);

namespace Temper\NullableProperties;

use Illuminate\Database\Eloquent\Model;

class NullableObserver
{
public function saving(Model $model): bool
{
if (!$model->nullable) return true;

foreach ($model->toArray() as $property => $value) {
if ($value === '' && $model->isDirty($property) && in_array($property, $model->nullable, true)) {
$model->$property = null;
}
}

return true;
}
}
19 changes: 19 additions & 0 deletions NullableProperties.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php declare(strict_types=1);

namespace Temper\NullableProperties;

use Illuminate\Database\Eloquent\Model;

/**
* Trait NullableProperties
* @package Temper\NullableProperties
* @mixin Model
*/
trait NullableProperties {

public static function bootNullableProperties(): void
{
static::observe(new NullableObserver());
}
}

25 changes: 25 additions & 0 deletions Providers/NullablePropertiesServiceProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php declare(strict_types=1);

namespace Temper\NullableProperties\Providers;

use Illuminate\Support\ServiceProvider;

class NullablePropertiesServiceProvider extends ServiceProvider
{
protected $defer;

public function boot(): void
{
//
}

public function register(): void
{
//
}

public function provides(): array
{
return [];
}
}
43 changes: 43 additions & 0 deletions Tests/NullablePropertiesTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php declare(strict_types=1);

namespace Temper\NullableProperties\Tests;

use Illuminate\Database\Eloquent\Model;
use PHPUnit\Framework\TestCase;
use Temper\NullableProperties\NullableProperties;

class NullablePropertiesTest extends TestCase
{
/** @dataProvider provides_values */
public function test_it_converts_empty_strings_to_null($propertyName, $initialValue, $expectedResult)
{
/** @var Model $model */
$model = new class() extends Model {
use NullableProperties;

protected $nullable = ['nullableProperty'];
public function save(array $options = []) { return $this->fireModelEvent('saving'); }
};

$model->$propertyName = $initialValue;

$this->assertEquals($initialValue, $model->$propertyName);
$this->assertEquals(true, $model->save());
$this->assertEquals($expectedResult, $model->$propertyName);
}

public function provides_values()
{
return [
['nullableProperty', '', null],
['nullableProperty', 'string', 'string'],
['nullableProperty', null, null],
['nullableProperty', [], []],
['nullableProperty', false, false],
['nullableProperty', 0, 0],
['otherProperty', '', ''],
];
}
}


37 changes: 37 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "temper/laravel-nullable-properties",
"description": "",
"authors": [
{
"name": "Temper",
"email": "info@temper.works"
}
],
"require": {
"php": ">=7.0"
},
"require-dev": {
"phpunit/phpunit": "*",
"orchestra/testbench": "^6.12"
},
"extra": {
"laravel": {
"providers": [
"Temper\\NullableProperties\\Providers\\NullablePropertiesServiceProvider"
],
"aliases": {

}
}
},
"autoload": {
"psr-4": {
"Temper\\NullableProperties\\": ""
}
},
"scripts": {
"test": [
"./vendor/bin/phpunit Tests/"
]
}
}
54 changes: 54 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Nullable Properties

This package provides automatically nulled properties for Eloquent Models.

When patching an Eloquent Model, it might be common to do something like this:

```php
$this->property = $request->get('property');
```

Or even:

```php
$this->fill($request->all());
```

A side effect of this is that empty fields in forms are stored in the database as empty strings, even when the database column is nullable.

This can interfere with future queries such as:

```
$users = User::whereNull('favoriteColor')->get();
```

## Usage

Usage is simple. Add:

* The `NullableProperties` trait
* An array named `$nullable`, containing Model property names (similar to arrays like `$fillable` and `$casts`):

```php
<?php

use Illuminate\Database\Eloquent\Model;
use Temper\NullableProperties\NullableProperties;

class User extends Model {
use NullableProperties;

public $nullable = ['favoriteColor'];
};
```

Properties in this array are not allowed to be stored as empty strings, they will always default back to `null` when empty.

Laravel can not easily detect nullable values at runtime. but you can easily get a table's nullable properties from the database:

```mysql
select concat('protected $nullable = [', group_concat(concat("'",column_name,"'")),'];')
from information_schema.columns
where table_name = 'users' and is_nullable = 'YES'
and data_type = 'varchar';
```

0 comments on commit aaa48a8

Please sign in to comment.