-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
219 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
/vendor | ||
composer.phar | ||
composer.lock | ||
.DS_Store | ||
.idea |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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'); | ||
} | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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": "[email protected]" | ||
} | ||
], | ||
"autoload": { | ||
"psr-4": { | ||
"Alfa\\EloquentHasManySync\\": "src" | ||
} | ||
}, | ||
"extra": { | ||
"laravel": { | ||
"providers": [ | ||
"Alfa\\EloquentHasManySync\\ServiceProvider" | ||
] | ||
} | ||
}, | ||
"minimum-stability": "dev", | ||
"prefer-stable": true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
}); | ||
} | ||
} |