-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
β
ππ¨ Add tests, fix bugs, and refactor Traits.
- Loading branch information
Showing
16 changed files
with
457 additions
and
96 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
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
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,8 @@ | ||
<?php | ||
|
||
namespace MathieuTu\JsonImport\Exceptions; | ||
|
||
class UnknownAttributeException extends \InvalidArgumentException | ||
{ | ||
|
||
} |
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,55 @@ | ||
<?php | ||
|
||
namespace MathieuTu\JsonImport\Helpers; | ||
|
||
use Illuminate\Database\Eloquent\Relations\HasOne; | ||
use Illuminate\Database\Eloquent\Relations\HasOneOrMany; | ||
use Illuminate\Support\Collection; | ||
use MathieuTu\JsonImport\Contracts\JsonExportable; | ||
|
||
class JsonExporter | ||
{ | ||
private $exportable; | ||
|
||
public function __construct(JsonExportable $exportable) | ||
{ | ||
$this->exportable = $exportable; | ||
} | ||
|
||
public static function exportToJson(JsonExportable $exportable, $options = 0) | ||
{ | ||
return self::exportToCollection($exportable)->toJson($options); | ||
} | ||
|
||
public static function exportToCollection(JsonExportable $exportable): Collection | ||
{ | ||
$helper = new static($exportable); | ||
|
||
return $helper->exportAttributes()->merge($helper->exportRelations()); | ||
} | ||
|
||
public function exportAttributes(): Collection | ||
{ | ||
return collect($this->exportable->getJsonExportableAttributes()) | ||
->mapWithKeys(function ($attribute) { | ||
return [$attribute => $this->exportable->$attribute]; | ||
}); | ||
} | ||
|
||
public function exportRelations(): Collection | ||
{ | ||
return collect($this->exportable->getJsonExportableRelations()) | ||
->mapWithKeys(function ($relationName) { | ||
return [$relationName => $this->exportable->$relationName()]; | ||
})->filter(function ($relationObject) { | ||
return $relationObject instanceof HasOneOrMany | ||
&& $relationObject->getRelated() instanceof JsonExportable; | ||
})->map(function (HasOneOrMany $relationObject) { | ||
$export = $relationObject->get()->map(function ($object) { | ||
return self::exportToCollection($object); | ||
}); | ||
|
||
return $relationObject instanceof HasOne ? $export->first() : $export; | ||
}); | ||
} | ||
} |
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,105 @@ | ||
<?php | ||
|
||
namespace MathieuTu\JsonImport\Helpers; | ||
|
||
|
||
use BadMethodCallException; | ||
use Illuminate\Database\Eloquent\Model; | ||
use Illuminate\Database\Eloquent\Relations\HasOneOrMany; | ||
use Illuminate\Support\Str; | ||
use MathieuTu\JsonImport\Contracts\JsonImportable; | ||
use MathieuTu\JsonImport\Exceptions\JsonDecodingException; | ||
use MathieuTu\JsonImport\Exceptions\UnknownAttributeException; | ||
|
||
class JsonImporter | ||
{ | ||
private $importable; | ||
|
||
public function __construct(JsonImportable $importable) | ||
{ | ||
$this->importable = $importable; | ||
} | ||
|
||
public function importFromJson($objects) | ||
{ | ||
$objects = $this->convertObjectsToArray($objects); | ||
|
||
$objects = $this->wrap($objects); | ||
|
||
foreach ($objects as $attributes) { | ||
$object = $this->importAttributes($attributes); | ||
|
||
$this->importRelations($object, $attributes); | ||
} | ||
} | ||
|
||
protected function convertObjectsToArray($objects): array | ||
{ | ||
if (is_string($objects)) { | ||
$objects = json_decode($objects, true); | ||
} | ||
|
||
if (json_last_error() !== JSON_ERROR_NONE) { | ||
throw new JsonDecodingException('Invalid json format.'); | ||
} | ||
return $objects; | ||
} | ||
|
||
protected function wrap($objects): array | ||
{ | ||
return is_array(array_values($objects)[0]) ? $objects : [$objects]; | ||
} | ||
|
||
protected function importAttributes($attributes): JsonImportable | ||
{ | ||
return $this->importable instanceof Model ? $object = $this->importable->create($attributes) : $this->importable; | ||
} | ||
|
||
protected function importRelations($object, $attributes) | ||
{ | ||
foreach ($this->importable->getJsonImportableRelations($attributes) as $relationName) { | ||
|
||
$this->importChildrenIfImportable($this->getRelationObject($object, $relationName), $this->wrap($attributes[$relationName])); | ||
} | ||
} | ||
|
||
protected function importChildrenIfImportable(HasOneOrMany $relation, array $children) | ||
{ | ||
$childClass = $relation->getRelated(); | ||
|
||
if ($childClass instanceof JsonImportable) { | ||
$children = $this->addParentKeyToChildren($children, $relation); | ||
|
||
$childClass->importFromJson($children); | ||
} | ||
} | ||
|
||
protected function addParentKeyToChildren(array $children, HasOneOrMany $relation): array | ||
{ | ||
return array_map(function ($object) use ($relation) { | ||
$object[$relation->getForeignKeyName()] = $relation->getParentKey(); | ||
|
||
return $object; | ||
}, $children); | ||
} | ||
|
||
protected function getRelationObject($object, $relationName) | ||
{ | ||
$relationName = Str::camel($relationName); | ||
|
||
try { | ||
$relation = $object->$relationName(); | ||
|
||
if (!$relation instanceof HasOneOrMany) { | ||
throw new BadMethodCallException(); | ||
} | ||
|
||
return $relation; | ||
} catch (BadMethodCallException $e) { | ||
throw new UnknownAttributeException('Unknown attribute or relation "' . $relationName . '" in "' . get_class($object) . '".'); | ||
} | ||
|
||
|
||
} | ||
|
||
} |
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
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 |
---|---|---|
@@ -1,81 +1,22 @@ | ||
<?php | ||
|
||
namespace MathieuTu\JsonImport\Traits; | ||
|
||
use Illuminate\Database\Eloquent\Model; | ||
use Illuminate\Database\Eloquent\Relations\HasOneOrMany; | ||
use MathieuTu\JsonImport\Contracts\JsonImportable; | ||
use MathieuTu\JsonImport\Exceptions\JsonDecodingException; | ||
|
||
use \MathieuTu\JsonImport\Helpers\JsonImporter as ImporterHelper; | ||
trait JsonImporter | ||
{ | ||
protected $jsonImportableRelations = []; | ||
|
||
public function importFromJson($objects) | ||
{ | ||
$objects = $this->convertObjectsToArray($objects); | ||
|
||
foreach ($objects as $attributes) { | ||
$object = $this->importAttributes($attributes); | ||
|
||
$this->importRelations($object, $attributes); | ||
} | ||
} | ||
|
||
protected function convertObjectsToArray($objects): mixed | ||
{ | ||
if (is_string($objects)) { | ||
$objects = json_decode($objects, true); | ||
} | ||
protected $jsonImportableRelations; | ||
|
||
if (json_last_error() !== JSON_ERROR_NONE) { | ||
throw new JsonDecodingException('Invalid json format.'); | ||
} | ||
return $objects; | ||
} | ||
|
||
protected function importAttributes($attributes): Model | ||
public static function importFromJson($objects) | ||
{ | ||
return $this instanceof Model ? $object = $this->create($attributes) : $this; | ||
} | ||
$importer = new ImporterHelper(new static); | ||
|
||
protected function importRelations($object, $attributes) | ||
{ | ||
foreach ($this->getJsonImportableRelations($object) as $relationName) { | ||
$this->importChildrenIfImportable($object->$relationName, $attributes[$relationName]); | ||
} | ||
$importer->importFromJson($objects); | ||
} | ||
|
||
public function getJsonImportableRelations($object): array | ||
public function getJsonImportableRelations($attributes = []): array | ||
{ | ||
return $this->jsonImportableRelations | ||
?? $this->jsonExportableRelations | ||
?? array_diff(array_keys($object), $this->getFillable()); | ||
} | ||
|
||
abstract public function getFillable(): array; | ||
|
||
protected function importChildrenIfImportable(HasOneOrMany $relation, array $children) | ||
{ | ||
if (!$relation instanceof HasOneOrMany) { | ||
return; | ||
} | ||
|
||
$childClass = $relation->getRelated(); | ||
|
||
if ($childClass instanceof JsonImportable) { | ||
$children = $this->addParentKeyToChildren($children, $relation); | ||
|
||
$childClass->importFromJson($children); | ||
} | ||
} | ||
|
||
protected function addParentKeyToChildren(array $children, HasOneOrMany $relation): array | ||
{ | ||
foreach ($children as $object) { | ||
$object[$relation->getForeignKeyName()] = $relation->getParentKey(); | ||
} | ||
|
||
return $children; | ||
?? array_diff(array_keys($attributes), $this->getFillable()); | ||
} | ||
} |
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,21 @@ | ||
<?php | ||
|
||
namespace MathieuTu\JsonImport\Tests\Stubs; | ||
|
||
use MathieuTu\JsonImport\Contracts\JsonExportable; | ||
use MathieuTu\JsonImport\Contracts\JsonImportable; | ||
use MathieuTu\JsonImport\Traits\JsonExporter; | ||
use MathieuTu\JsonImport\Traits\JsonImporter; | ||
|
||
class Bar extends Model implements JsonExportable, JsonImportable | ||
{ | ||
use JsonExporter, JsonImporter; | ||
|
||
protected $fillable = ['name', 'foo_id']; | ||
|
||
public function baz() | ||
{ | ||
return $this->hasOne(Baz::class); | ||
} | ||
|
||
} |
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,22 @@ | ||
<?php | ||
|
||
namespace MathieuTu\JsonImport\Tests\Stubs; | ||
|
||
use MathieuTu\JsonImport\Contracts\JsonExportable; | ||
use MathieuTu\JsonImport\Contracts\JsonImportable; | ||
use MathieuTu\JsonImport\Traits\JsonExporter; | ||
use MathieuTu\JsonImport\Traits\JsonImporter; | ||
|
||
class Baz extends Model implements JsonExportable, JsonImportable | ||
{ | ||
use JsonExporter, JsonImporter; | ||
|
||
protected $fillable = ['name', 'bar_id']; | ||
|
||
public function doNots() | ||
{ | ||
return $this->hasMany(DoNotExport::class); | ||
} | ||
|
||
} | ||
|
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,12 @@ | ||
<?php | ||
namespace MathieuTu\JsonImport\Tests\Stubs; | ||
|
||
use MathieuTu\JsonImport\Contracts\JsonExportable; | ||
use MathieuTu\JsonImport\Traits\JsonExporter; | ||
|
||
class DoNotExport extends Model | ||
{ | ||
// This one isn't JsonExportable | ||
protected $fillable = ['name', 'baz_id']; | ||
|
||
} |
Oops, something went wrong.