Skip to content

Instantly share code, notes, and snippets.

@greabock
Created September 16, 2016 10:27
Show Gist options
  • Save greabock/ff4a33d20e9b10db86c63d1851d99038 to your computer and use it in GitHub Desktop.
Save greabock/ff4a33d20e9b10db86c63d1851d99038 to your computer and use it in GitHub Desktop.
Doctrine Mapper
<?php
namespace App\Services;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping\ClassMetadataInfo as MetaInfo;
use RuntimeException;
class Mapper
{
/**
* @var EntityManager
*/
protected $em;
/**
* @var callable
*/
protected $factory;
public function __construct(EntityManager $em, callable $factory)
{
$this->em = $em;
$this->factory = $factory;
}
public function json(string $name, $data)
{
$properties = json_decode($data, true);
return $this->map($name, $properties);
}
public function map(string $name, array $data, $id = null)
{
$meta = $this->em->getClassMetadata($name);
$identifier = array_first($meta->getIdentifier());
$identifier = array_pull($data, $identifier);
$id = $id ?: $identifier;
return $this->buildEntity($meta, $data, $id);
}
/**
* TODO: распилить на части.
*
* @param MetaInfo $meta
* @param array $data
* @param null $id
*
* @return mixed|null|object
*/
private function buildEntity(MetaInfo $meta, array $data, $id = null)
{
$entity = $this->resolveEntity($meta->getName(), $id);
$relationsMaps = array_only($data, $meta->getAssociationNames());
$entityFields = array_only($data, $meta->getFieldNames());
$setters = [];
foreach ($relationsMaps as $name => $map) {
$setterName = $this->getSetterName($name);
if (!method_exists($entity, $setterName)) {
continue;
}
$mapping = $meta->getAssociationMapping($name);
$setters[$setterName] =
$this->mapRelation($mapping['type'], $mapping['targetEntity'], $map);
}
foreach ($entityFields as $name => $value) {
$setterName = $this->getSetterName($name);
if (!method_exists($entity, $setterName)) {
continue;
}
$setters[$setterName] = $value;
}
$this->fill($entity, $setters);
return $entity;
}
private function resolveEntity($entity, $id = null)
{
if (!is_null($id)) {
$identity = $this->em->find($entity, $id);
if (is_null($identity)) {
throw new RuntimeException(
"Entity {$entity} with identifier '{$id}' not found"
);
}
return $identity;
}
return call_user_func($this->factory, $entity);
}
private function isToMany($type)
{
return $type & (MetaInfo::ONE_TO_MANY | MetaInfo::MANY_TO_MANY);
}
private function mapMany($name, $data)
{
return array_map(function ($item) use ($name) {
return $this->map($name, $item);
}, $data);
}
private function mapRelation($type, $targetEntity, $map)
{
if ($this->isToMany($type)) {
return $this->mapMany($targetEntity, $map);
}
return $this->map($targetEntity, $map);
}
private function fill($entity, $setters)
{
foreach ($setters as $setter => $value) {
call_user_func([$entity, $setter], $value);
}
}
private function getSetterName($name)
{
return 'set' . studly_case($name);
}
}
@greabock
Copy link
Author

greabock commented Sep 16, 2016

Laravel binding factory sample

    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->bind(Mapper::class, function (Container $app) {
            return $app->build(Mapper::class, ['factory' => function ($buildable) use ($app) {
                return $app->build($buildable);
            }]);
        });
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment