Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
alfa6661 committed Aug 10, 2018
1 parent bd08ab6 commit 3b4c594
Show file tree
Hide file tree
Showing 4 changed files with 219 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/vendor
composer.phar
composer.lock
.DS_Store
.idea
101 changes: 101 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Laravel HasMany Sync

Allow sync method for Laravel Has Many Relationship.

# Installation

You can install the package via composer:

```
composer require alfa/laravel-has-many-sync
```

Register the ServiceProvider in `config/app.php`

```php
'providers' => [
// ...
Alfa\EloquentHasManySync\ServiceProvider::class,
],
```

# Usage


## Setup HasMany Relation

```php
class Customer extends Model
{
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function contacts()
{
return $this->hasMany(CustomerContact::class);
}
}
```

You can access the sync method like this:

```php
$customer->contacts()->sync([
[
'id' => 1,
'name' => 'Alfa',
'phone_number' => '123',
],
[
'id' => null,
'name' => 'Adhitya',
'phone_number' => '234,
]
]);
```

The sync method accepts an array of data to place on the intermediate table. Any data that are not in the given array will be removed from the intermediate table. So, after this operation is complete, only the data in the given array will exist in the intermediate table:

### Syncing without deleting

If you do not want to delete existing data, you may pass false value to the second parameter in the sync method.

```php
$customer->contacts()->sync([
[
'id' => 1,
'name' => 'Alfa',
'phone_number' => '123',
],
[
'id' => null,
'name' => 'Adhitya',
'phone_number' => '234,
]
], false);
```


### Example usage in the controller.

```php
class CustomersController extends Controller
{
/**
* Update the specified resource in storage.
*
* @param CustomerRequest $request
* @param Customer $customer
* @return \Illuminate\Http\Response
*/
public function update(CustomerRequest $request, Customer $customer)
{
DB::transaction(function () use ($customer, $request) {
$customer->update($request->all());
$customer->contacts()->sync($request->get('contacts', []));
});

return redirect()->route('customers.index');
}
}
```
29 changes: 29 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "alfa/laravel-has-many-sync",
"description": "Laravel has many sync",
"type": "library",
"require": {
"illuminate/support": "~5.4"
},
"license": "MIT",
"authors": [
{
"name": "Alfa Adhitya",
"email": "alfa2159@gmail.com"
}
],
"autoload": {
"psr-4": {
"Alfa\\EloquentHasManySync\\": "src"
}
},
"extra": {
"laravel": {
"providers": [
"Alfa\\EloquentHasManySync\\ServiceProvider"
]
}
},
"minimum-stability": "dev",
"prefer-stable": true
}
84 changes: 84 additions & 0 deletions src/ServiceProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php

namespace Alfa\EloquentHasManySync;

use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\ServiceProvider as BaseServiceProvider;

class ServiceProvider extends BaseServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
HasMany::macro('sync', function (array $data, $deleting = true) {
$changes = [
'created' => [], 'deleted' => [], 'updated' => [],
];

/** @var HasMany $this */

// Get the primary key.
$relatedKeyName = $this->getRelated()->getKeyName();

// Get the current key values.
$current = $this->newQuery()->pluck($relatedKeyName)->all();

// Cast the given key to an integer if it is numeric.
$castKey = function ($value) {
if (is_null($value)) {
return $value;
}

return is_numeric($value) ? (int) $value : (string) $value;
};

// Cast the given keys to integers if they are numeric and string otherwise.
$castKeys = function ($keys) use ($castKey) {
return (array) array_map(function ($key) use ($castKey) {
return $castKey($key);
}, $keys);
};

// Get any non-matching rows.
$deletedKeys = array_diff($current, $castKeys(
array_pluck($data, $relatedKeyName))
);

if ($deleting && count($deletedKeys) > 0) {
$this->getRelated()->destroy($deletedKeys);
$changes['deleted'] = $deletedKeys;
}

// Separate the submitted data into "update" and "new"
// We determine "newRows" as those whose $relatedKeyName (usually 'id') is null.
$newRows = array_where($data, function ($row) use ($relatedKeyName) {
return array_get($row, $relatedKeyName) === null;
});

// We determine "updateRows" as those whose $relatedKeyName (usually 'id') is set, not null.
$updatedRows = array_where($data, function ($row) use ($relatedKeyName) {
return array_get($row, $relatedKeyName) !== null;
});

if (count($newRows) > 0) {
$newRecords = $this->createMany($newRows);
$changes['created'] = $castKeys(
$newRecords->pluck($relatedKeyName)->toArray()
);
}

foreach ($updatedRows as $row) {
$this->getRelated()->where($relatedKeyName, $castKey(array_get($row, $relatedKeyName)))
->update($row);
}

$changes['updated'] = $castKeys(array_pluck($updatedRows, $relatedKeyName));

return $changes;
});
}
}

0 comments on commit 3b4c594

Please sign in to comment.