Skip to content

Commit

Permalink
Merge pull request #54 from mrakolice/schema-defaults
Browse files Browse the repository at this point in the history
Add possibility to change default schema classes
  • Loading branch information
wolfy-j committed Jan 29, 2020
2 parents 07f27fb + 52f2db1 commit cc2af68
Show file tree
Hide file tree
Showing 6 changed files with 408 additions and 49 deletions.
112 changes: 100 additions & 12 deletions src/Factory.php
Expand Up @@ -12,9 +12,14 @@
namespace Cycle\ORM;

use Cycle\ORM\Config\RelationConfig;
use Cycle\ORM\Exception\TypecastException;
use Cycle\ORM\Mapper\Mapper;
use Cycle\ORM\Relation\RelationInterface;
use Cycle\ORM\Select\ConstrainInterface;
use Cycle\ORM\Select\LoaderInterface;
use Cycle\ORM\Select\Repository;
use Cycle\ORM\Select\Source;
use Cycle\ORM\Select\SourceInterface;
use Psr\Container\ContainerInterface;
use Spiral\Core\Container;
use Spiral\Core\FactoryInterface as CoreFactory;
Expand All @@ -35,11 +40,14 @@ final class Factory implements FactoryInterface
/** @var DatabaseProviderInterface */
private $dbal;

/** @var array<string, string> */
private $defaults = [];

/**
* @param DatabaseProviderInterface $dbal
* @param RelationConfig $config
* @param CoreFactory|null $factory
* @param ContainerInterface|null $container
* @param RelationConfig $config
* @param CoreFactory|null $factory
* @param ContainerInterface|null $container
*/
public function __construct(
DatabaseProviderInterface $dbal,
Expand All @@ -51,13 +59,21 @@ public function __construct(
$this->config = $config ?? RelationConfig::getDefault();
$this->factory = $factory ?? new Container();
$this->container = $container ?? new Container();
$this->defaults = [
Schema::REPOSITORY => Repository::class,
Schema::SOURCE => Source::class,
Schema::MAPPER => Mapper::class,
Schema::CONSTRAIN => null,
];
}

/**
* @inheritdoc
*/
public function make(string $alias, array $parameters = [])
{
public function make(
string $alias,
array $parameters = []
) {
return $this->factory->make($alias, $parameters);
}

Expand All @@ -69,11 +85,15 @@ public function mapper(
SchemaInterface $schema,
string $role
): MapperInterface {
$class = $schema->define($role, Schema::MAPPER) ?? Mapper::class;
$class = $schema->define($role, Schema::MAPPER) ?? $this->defaults[Schema::MAPPER];

if (!is_subclass_of($class, MapperInterface::class)) {
throw new TypecastException($class . ' not implement MapperInterface');
}

return $this->factory->make($class, [
'orm' => $orm,
'role' => $role,
'orm' => $orm,
'role' => $role,
'schema' => $schema->define($role, Schema::SCHEMA)
]);
}
Expand All @@ -90,8 +110,8 @@ public function loader(
$schema = $schema->defineRelation($role, $relation);

return $this->config->getLoader($schema[Relation::TYPE])->resolve($this->factory, [
'orm' => $orm,
'name' => $relation,
'orm' => $orm,
'name' => $relation,
'target' => $schema[Relation::TARGET],
'schema' => $schema[Relation::SCHEMA]
]);
Expand All @@ -110,8 +130,8 @@ public function relation(
$type = $relSchema[Relation::TYPE];

return $this->config->getRelation($type)->resolve($this->factory, [
'orm' => $orm,
'name' => $relation,
'orm' => $orm,
'name' => $relation,
'target' => $relSchema[Relation::TARGET],
'schema' => $relSchema[Relation::SCHEMA] + [Relation::LOAD => $relSchema[Relation::LOAD] ?? null],
]);
Expand All @@ -124,4 +144,72 @@ public function database(string $database = null): DatabaseInterface
{
return $this->dbal->database($database);
}

/**
* @inheritDoc
*/
public function repository(
ORMInterface $orm,
SchemaInterface $schema,
string $role,
?Select $select
): RepositoryInterface {
$class = $schema->define($role, Schema::REPOSITORY) ?? $this->defaults[Schema::REPOSITORY];

if (!is_subclass_of($class, RepositoryInterface::class)) {
throw new TypecastException($class . ' not implement RepositoryInterface');
}

return $this->factory->make($class, ['select' => $select, 'orm' => $orm]);
}

/**
* @inheritDoc
*/
public function source(
ORMInterface $orm,
SchemaInterface $schema,
string $role
): SourceInterface {
$source = $schema->define($role, Schema::SOURCE) ?? $this->defaults[Schema::SOURCE];

if (!is_subclass_of($source, SourceInterface::class)) {
throw new TypecastException($source . ' not implement SourceInterface');
}

if ($source !== Source::class) {
return $this->factory->make($source, ['orm' => $orm, 'role' => $role]);
}

$source = new Source(
$this->database($schema->define($role, Schema::DATABASE)),
$schema->define($role, Schema::TABLE)
);

$constrain = $schema->define($role, Schema::CONSTRAIN) ?? $this->defaults[Schema::CONSTRAIN];

if ($constrain === null) {
return $source;
}

if (!is_subclass_of($constrain, ConstrainInterface::class)) {
throw new TypecastException($constrain . ' not implement ConstrainInterface');
}

return $source->withConstrain(is_object($constrain) ? $constrain : $this->factory->make($constrain));
}

/**
* Add default classes for resolve
* @param array $defaults
* @return FactoryInterface
*/
public function withDefaultSchemaClasses(array $defaults): FactoryInterface
{
$clone = clone $this;

$clone->defaults = $defaults + $this->defaults;

return $clone;
}
}
48 changes: 40 additions & 8 deletions src/FactoryInterface.php
Expand Up @@ -13,6 +13,7 @@

use Cycle\ORM\Relation\RelationInterface;
use Cycle\ORM\Select\LoaderInterface;
use Cycle\ORM\Select\SourceInterface;
use Spiral\Core\FactoryInterface as CoreFactory;
use Spiral\Database\DatabaseProviderInterface;

Expand All @@ -24,9 +25,9 @@ interface FactoryInterface extends DatabaseProviderInterface, CoreFactory
/**
* Create mapper associated with given role.
*
* @param ORMInterface $orm
* @param ORMInterface $orm
* @param SchemaInterface $schema
* @param string $role
* @param string $role
* @return MapperInterface
*/
public function mapper(
Expand All @@ -38,10 +39,10 @@ public function mapper(
/**
* Create loader associated with specific entity and relation.
*
* @param ORMInterface $orm
* @param ORMInterface $orm
* @param SchemaInterface $schema
* @param string $role
* @param string $relation
* @param string $role
* @param string $relation
* @return LoaderInterface
*/
public function loader(
Expand All @@ -51,13 +52,44 @@ public function loader(
string $relation
): LoaderInterface;


/**
* Create repository associated with given role,
*
* @param ORMInterface $orm
* @param SchemaInterface $schema
* @param string $role
* @param Select $select
* @return RepositoryInterface
*/
public function repository(
ORMInterface $orm,
SchemaInterface $schema,
string $role,
?Select $select
): RepositoryInterface;

/**
* Create source associated with given role
*
* @param ORMInterface $orm
* @param SchemaInterface $schema
* @param string $role
* @return SourceInterface
*/
public function source(
ORMInterface $orm,
SchemaInterface $schema,
string $role
): SourceInterface;

/**
* Create relation associated with specific entity and relation.
*
* @param ORMInterface $orm
* @param ORMInterface $orm
* @param SchemaInterface $schema
* @param string $role
* @param string $relation
* @param string $role
* @param string $relation
* @return RelationInterface
*/
public function relation(
Expand Down
36 changes: 7 additions & 29 deletions src/ORM.php
Expand Up @@ -20,8 +20,6 @@
use Cycle\ORM\Heap\Node;
use Cycle\ORM\Promise\Reference;
use Cycle\ORM\Promise\ReferenceInterface;
use Cycle\ORM\Select\Repository;
use Cycle\ORM\Select\Source;
use Cycle\ORM\Select\SourceInterface;

/**
Expand Down Expand Up @@ -60,7 +58,7 @@ final class ORM implements ORMInterface
private $sources = [];

/**
* @param FactoryInterface $factory
* @param FactoryInterface $factory
* @param SchemaInterface|null $schema
*/
public function __construct(FactoryInterface $factory, SchemaInterface $schema = null)
Expand Down Expand Up @@ -263,16 +261,14 @@ public function getRepository($entity): RepositoryInterface
return $this->repositories[$role];
}

// todo: alter default repository
$repository = $this->getSchema()->define($role, Schema::REPOSITORY) ?? Repository::class;
$params = ['orm' => $this, 'role' => $role];
$select = null;

if ($this->getSchema()->define($role, Schema::TABLE) !== null) {
$params['select'] = new Select($this, $role);
$params['select']->constrain($this->getSource($role)->getConstrain());
if ($this->schema->define($role, Schema::TABLE) !== null) {
$select = new Select($this, $role);
$select->constrain($this->getSource($role)->getConstrain());
}

return $this->repositories[$role] = $this->factory->make($repository, $params);
return $this->repositories[$role] = $this->factory->repository($this, $this->schema, $role, $select);
}

/**
Expand All @@ -284,25 +280,7 @@ public function getSource(string $role): SourceInterface
return $this->sources[$role];
}

$source = $this->schema->define($role, Schema::SOURCE) ?? Source::class;
if ($source !== Source::class) {
// custom implementation
return $this->factory->make($source, ['orm' => $this, 'role' => $role]);
}

$source = new Source(
$this->factory->database($this->schema->define($role, Schema::DATABASE)),
(string)$this->schema->define($role, Schema::TABLE)
);

$constrain = $this->schema->define($role, Schema::CONSTRAIN);
if ($constrain !== null) {
$source = $source->withConstrain(
is_object($constrain) ? $constrain : $this->factory->make((string)$constrain)
);
}

return $this->sources[$role] = $source;
return $this->sources[$role] = $this->factory->source($this, $this->schema, $role);
}

/**
Expand Down
74 changes: 74 additions & 0 deletions src/Select/PersistRepository.php
@@ -0,0 +1,74 @@
<?php

namespace Cycle\ORM\Select;

use Cycle\ORM\ORMInterface;
use Cycle\ORM\Select;
use Cycle\ORM\Transaction;

class PersistRepository extends Repository
{
/** @var Transaction */
private $transaction;

/**
* @param Select $select
* @param ORMInterface $orm
*/
public function __construct(
Select $select,
ORMInterface $orm
) {
parent::__construct($select);
$this->transaction = new Transaction($orm);
}

/**
* @param mixed $entity
* @param bool $cascade
*
* @throws \Throwable
*/
public function save(
$entity,
bool $cascade = true
): void {
$this->transaction->persist(
$entity,
$cascade ? Transaction::MODE_CASCADE : Transaction::MODE_ENTITY_ONLY
);

$this->transaction->run(); // transaction is clean after run
}

/**
* @param mixed $entity
* @param bool $cascade
*
* @throws \Throwable
*/
public function delete(
$entity,
bool $cascade = true
): void {
$this->transaction->persist(
$entity,
$cascade ? Transaction::MODE_CASCADE : Transaction::MODE_ENTITY_ONLY
);

$this->transaction->run(); // transaction is clean after run
}

/**
* @param $id
* @param bool $cascade
*
* @throws \Throwable
*/
public function deleteByPK(
$id,
bool $cascade = true
): void {
$this->delete($this->findByPK($id), $cascade);
}
}

0 comments on commit cc2af68

Please sign in to comment.